import from github
This commit is contained in:
1249
gflib/bg.c
Normal file
1249
gflib/bg.c
Normal file
@ -0,0 +1,1249 @@
|
||||
#include <limits.h>
|
||||
#include "global.h"
|
||||
#include "bg.h"
|
||||
#include "dma3.h"
|
||||
#include "gpu_regs.h"
|
||||
|
||||
#define DISPCNT_ALL_BG_AND_MODE_BITS (DISPCNT_BG_ALL_ON | 0x7)
|
||||
|
||||
struct BgControl
|
||||
{
|
||||
struct BgConfig {
|
||||
u8 visible:1;
|
||||
u8 unknown_1:1;
|
||||
u8 screenSize:2;
|
||||
u8 priority:2;
|
||||
u8 mosaic:1;
|
||||
u8 wraparound:1;
|
||||
|
||||
u8 charBaseIndex:2;
|
||||
u8 mapBaseIndex:5;
|
||||
u8 paletteMode:1;
|
||||
|
||||
u8 unknown_2; // Assigned to but never read
|
||||
u8 unknown_3; // Assigned to but never read
|
||||
} configs[NUM_BACKGROUNDS];
|
||||
|
||||
u16 bgVisibilityAndMode;
|
||||
};
|
||||
|
||||
struct BgConfig2
|
||||
{
|
||||
u32 baseTile:10;
|
||||
u32 basePalette:4;
|
||||
u32 unk_3:18;
|
||||
|
||||
void* tilemap;
|
||||
s32 bg_x;
|
||||
s32 bg_y;
|
||||
};
|
||||
|
||||
static struct BgControl sGpuBgConfigs;
|
||||
static struct BgConfig2 sGpuBgConfigs2[NUM_BACKGROUNDS];
|
||||
static u32 sDmaBusyBitfield[NUM_BACKGROUNDS];
|
||||
|
||||
u32 gWindowTileAutoAllocEnabled;
|
||||
|
||||
static const struct BgConfig sZeroedBgControlStruct = { 0 };
|
||||
|
||||
static u32 GetBgType(u8 bg);
|
||||
|
||||
void ResetBgs(void)
|
||||
{
|
||||
ResetBgControlStructs();
|
||||
sGpuBgConfigs.bgVisibilityAndMode = 0;
|
||||
SetTextModeAndHideBgs();
|
||||
}
|
||||
|
||||
static void SetBgModeInternal(u8 bgMode)
|
||||
{
|
||||
sGpuBgConfigs.bgVisibilityAndMode &= ~0x7;
|
||||
sGpuBgConfigs.bgVisibilityAndMode |= bgMode;
|
||||
}
|
||||
|
||||
u8 GetBgMode(void)
|
||||
{
|
||||
return sGpuBgConfigs.bgVisibilityAndMode & 0x7;
|
||||
}
|
||||
|
||||
void ResetBgControlStructs(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_BACKGROUNDS; i++)
|
||||
{
|
||||
sGpuBgConfigs.configs[i] = sZeroedBgControlStruct;
|
||||
}
|
||||
}
|
||||
|
||||
void Unused_ResetBgControlStruct(u8 bg)
|
||||
{
|
||||
if (!IsInvalidBg(bg))
|
||||
{
|
||||
sGpuBgConfigs.configs[bg] = sZeroedBgControlStruct;
|
||||
}
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
BG_CTRL_ATTR_VISIBLE = 1,
|
||||
BG_CTRL_ATTR_CHARBASEINDEX = 2,
|
||||
BG_CTRL_ATTR_MAPBASEINDEX = 3,
|
||||
BG_CTRL_ATTR_SCREENSIZE = 4,
|
||||
BG_CTRL_ATTR_PALETTEMODE = 5,
|
||||
BG_CTRL_ATTR_PRIORITY = 6,
|
||||
BG_CTRL_ATTR_MOSAIC = 7,
|
||||
BG_CTRL_ATTR_WRAPAROUND = 8,
|
||||
};
|
||||
|
||||
static void SetBgControlAttributes(u8 bg, u8 charBaseIndex, u8 mapBaseIndex, u8 screenSize, u8 paletteMode, u8 priority, u8 mosaic, u8 wraparound)
|
||||
{
|
||||
if (!IsInvalidBg(bg))
|
||||
{
|
||||
if (charBaseIndex != 0xFF)
|
||||
{
|
||||
sGpuBgConfigs.configs[bg].charBaseIndex = charBaseIndex;
|
||||
}
|
||||
|
||||
if (mapBaseIndex != 0xFF)
|
||||
{
|
||||
sGpuBgConfigs.configs[bg].mapBaseIndex = mapBaseIndex;
|
||||
}
|
||||
|
||||
if (screenSize != 0xFF)
|
||||
{
|
||||
sGpuBgConfigs.configs[bg].screenSize = screenSize;
|
||||
}
|
||||
|
||||
if (paletteMode != 0xFF)
|
||||
{
|
||||
sGpuBgConfigs.configs[bg].paletteMode = paletteMode;
|
||||
}
|
||||
|
||||
if (priority != 0xFF)
|
||||
{
|
||||
sGpuBgConfigs.configs[bg].priority = priority;
|
||||
}
|
||||
|
||||
if (mosaic != 0xFF)
|
||||
{
|
||||
sGpuBgConfigs.configs[bg].mosaic = mosaic;
|
||||
}
|
||||
|
||||
if (wraparound != 0xFF)
|
||||
{
|
||||
sGpuBgConfigs.configs[bg].wraparound = wraparound;
|
||||
}
|
||||
|
||||
sGpuBgConfigs.configs[bg].unknown_2 = 0;
|
||||
sGpuBgConfigs.configs[bg].unknown_3 = 0;
|
||||
|
||||
sGpuBgConfigs.configs[bg].visible = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static u16 GetBgControlAttribute(u8 bg, u8 attributeId)
|
||||
{
|
||||
if (!IsInvalidBg(bg) && sGpuBgConfigs.configs[bg].visible)
|
||||
{
|
||||
switch (attributeId)
|
||||
{
|
||||
case BG_CTRL_ATTR_VISIBLE:
|
||||
return sGpuBgConfigs.configs[bg].visible;
|
||||
case BG_CTRL_ATTR_CHARBASEINDEX:
|
||||
return sGpuBgConfigs.configs[bg].charBaseIndex;
|
||||
case BG_CTRL_ATTR_MAPBASEINDEX:
|
||||
return sGpuBgConfigs.configs[bg].mapBaseIndex;
|
||||
case BG_CTRL_ATTR_SCREENSIZE:
|
||||
return sGpuBgConfigs.configs[bg].screenSize;
|
||||
case BG_CTRL_ATTR_PALETTEMODE:
|
||||
return sGpuBgConfigs.configs[bg].paletteMode;
|
||||
case BG_CTRL_ATTR_PRIORITY:
|
||||
return sGpuBgConfigs.configs[bg].priority;
|
||||
case BG_CTRL_ATTR_MOSAIC:
|
||||
return sGpuBgConfigs.configs[bg].mosaic;
|
||||
case BG_CTRL_ATTR_WRAPAROUND:
|
||||
return sGpuBgConfigs.configs[bg].wraparound;
|
||||
}
|
||||
}
|
||||
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
u8 LoadBgVram(u8 bg, const void *src, u16 size, u16 destOffset, u8 mode)
|
||||
{
|
||||
u16 offset;
|
||||
s8 cursor;
|
||||
|
||||
if (IsInvalidBg(bg) || !sGpuBgConfigs.configs[bg].visible)
|
||||
return -1;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case 0x1:
|
||||
offset = sGpuBgConfigs.configs[bg].charBaseIndex * BG_CHAR_SIZE;
|
||||
offset = destOffset + offset;
|
||||
cursor = RequestDma3Copy(src, (void*)(offset + BG_VRAM), size, 0);
|
||||
if (cursor == -1)
|
||||
return -1;
|
||||
break;
|
||||
case 0x2:
|
||||
offset = sGpuBgConfigs.configs[bg].mapBaseIndex * BG_SCREEN_SIZE;
|
||||
offset = destOffset + offset;
|
||||
cursor = RequestDma3Copy(src, (void*)(offset + BG_VRAM), size, 0);
|
||||
if (cursor == -1)
|
||||
return -1;
|
||||
break;
|
||||
default:
|
||||
cursor = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
return cursor;
|
||||
}
|
||||
|
||||
static void ShowBgInternal(u8 bg)
|
||||
{
|
||||
u16 value;
|
||||
if (!IsInvalidBg(bg) && sGpuBgConfigs.configs[bg].visible)
|
||||
{
|
||||
value = sGpuBgConfigs.configs[bg].priority |
|
||||
(sGpuBgConfigs.configs[bg].charBaseIndex << 2) |
|
||||
(sGpuBgConfigs.configs[bg].mosaic << 6) |
|
||||
(sGpuBgConfigs.configs[bg].paletteMode << 7) |
|
||||
(sGpuBgConfigs.configs[bg].mapBaseIndex << 8) |
|
||||
(sGpuBgConfigs.configs[bg].wraparound << 13) |
|
||||
(sGpuBgConfigs.configs[bg].screenSize << 14);
|
||||
|
||||
SetGpuReg((bg << 1) + REG_OFFSET_BG0CNT, value);
|
||||
|
||||
sGpuBgConfigs.bgVisibilityAndMode |= 1 << (bg + 8);
|
||||
sGpuBgConfigs.bgVisibilityAndMode &= DISPCNT_ALL_BG_AND_MODE_BITS;
|
||||
}
|
||||
}
|
||||
|
||||
static void HideBgInternal(u8 bg)
|
||||
{
|
||||
if (!IsInvalidBg(bg))
|
||||
{
|
||||
sGpuBgConfigs.bgVisibilityAndMode &= ~(1 << (bg + 8));
|
||||
sGpuBgConfigs.bgVisibilityAndMode &= DISPCNT_ALL_BG_AND_MODE_BITS;
|
||||
}
|
||||
}
|
||||
|
||||
static void SyncBgVisibilityAndMode(void)
|
||||
{
|
||||
SetGpuReg(REG_OFFSET_DISPCNT, (GetGpuReg(REG_OFFSET_DISPCNT) & ~DISPCNT_ALL_BG_AND_MODE_BITS) | sGpuBgConfigs.bgVisibilityAndMode);
|
||||
}
|
||||
|
||||
void SetTextModeAndHideBgs(void)
|
||||
{
|
||||
SetGpuReg(REG_OFFSET_DISPCNT, GetGpuReg(REG_OFFSET_DISPCNT) & ~DISPCNT_ALL_BG_AND_MODE_BITS);
|
||||
}
|
||||
|
||||
static void SetBgAffineInternal(u8 bg, s32 srcCenterX, s32 srcCenterY, s16 dispCenterX, s16 dispCenterY, s16 scaleX, s16 scaleY, u16 rotationAngle)
|
||||
{
|
||||
struct BgAffineSrcData src;
|
||||
struct BgAffineDstData dest;
|
||||
|
||||
switch (sGpuBgConfigs.bgVisibilityAndMode & 0x7)
|
||||
{
|
||||
default:
|
||||
case 0:
|
||||
return;
|
||||
case 1:
|
||||
if (bg != 2)
|
||||
return;
|
||||
break;
|
||||
case 2:
|
||||
if (bg != 2 && bg != 3)
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
src.texX = srcCenterX;
|
||||
src.texY = srcCenterY;
|
||||
src.scrX = dispCenterX;
|
||||
src.scrY = dispCenterY;
|
||||
src.sx = scaleX;
|
||||
src.sy = scaleY;
|
||||
src.alpha = rotationAngle;
|
||||
|
||||
BgAffineSet(&src, &dest, 1);
|
||||
|
||||
SetGpuReg(REG_OFFSET_BG2PA, dest.pa);
|
||||
SetGpuReg(REG_OFFSET_BG2PB, dest.pb);
|
||||
SetGpuReg(REG_OFFSET_BG2PC, dest.pc);
|
||||
SetGpuReg(REG_OFFSET_BG2PD, dest.pd);
|
||||
SetGpuReg(REG_OFFSET_BG2PA, dest.pa);
|
||||
SetGpuReg(REG_OFFSET_BG2X_L, (s16)(dest.dx));
|
||||
SetGpuReg(REG_OFFSET_BG2X_H, (s16)(dest.dx >> 16));
|
||||
SetGpuReg(REG_OFFSET_BG2Y_L, (s16)(dest.dy));
|
||||
SetGpuReg(REG_OFFSET_BG2Y_H, (s16)(dest.dy >> 16));
|
||||
}
|
||||
|
||||
bool8 IsInvalidBg(u8 bg)
|
||||
{
|
||||
if (bg >= NUM_BACKGROUNDS)
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// From FRLG. Dummied out.
|
||||
int BgTileAllocOp(int bg, int offset, int count, int mode)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ResetBgsAndClearDma3BusyFlags(u32 leftoverFireRedLeafGreenVariable)
|
||||
{
|
||||
int i;
|
||||
ResetBgs();
|
||||
|
||||
for (i = 0; i < NUM_BACKGROUNDS; i++)
|
||||
{
|
||||
sDmaBusyBitfield[i] = 0;
|
||||
}
|
||||
|
||||
gWindowTileAutoAllocEnabled = leftoverFireRedLeafGreenVariable;
|
||||
}
|
||||
|
||||
void InitBgsFromTemplates(u8 bgMode, const struct BgTemplate *templates, u8 numTemplates)
|
||||
{
|
||||
int i;
|
||||
u8 bg;
|
||||
|
||||
SetBgModeInternal(bgMode);
|
||||
ResetBgControlStructs();
|
||||
|
||||
for (i = 0; i < numTemplates; i++)
|
||||
{
|
||||
bg = templates[i].bg;
|
||||
if (bg < NUM_BACKGROUNDS)
|
||||
{
|
||||
SetBgControlAttributes(bg,
|
||||
templates[i].charBaseIndex,
|
||||
templates[i].mapBaseIndex,
|
||||
templates[i].screenSize,
|
||||
templates[i].paletteMode,
|
||||
templates[i].priority,
|
||||
0,
|
||||
0);
|
||||
|
||||
sGpuBgConfigs2[bg].baseTile = templates[i].baseTile;
|
||||
sGpuBgConfigs2[bg].basePalette = 0;
|
||||
sGpuBgConfigs2[bg].unk_3 = 0;
|
||||
|
||||
sGpuBgConfigs2[bg].tilemap = NULL;
|
||||
sGpuBgConfigs2[bg].bg_x = 0;
|
||||
sGpuBgConfigs2[bg].bg_y = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InitBgFromTemplate(const struct BgTemplate *template)
|
||||
{
|
||||
u8 bg = template->bg;
|
||||
|
||||
if (bg < NUM_BACKGROUNDS)
|
||||
{
|
||||
SetBgControlAttributes(bg,
|
||||
template->charBaseIndex,
|
||||
template->mapBaseIndex,
|
||||
template->screenSize,
|
||||
template->paletteMode,
|
||||
template->priority,
|
||||
0,
|
||||
0);
|
||||
|
||||
sGpuBgConfigs2[bg].baseTile = template->baseTile;
|
||||
sGpuBgConfigs2[bg].basePalette = 0;
|
||||
sGpuBgConfigs2[bg].unk_3 = 0;
|
||||
|
||||
sGpuBgConfigs2[bg].tilemap = NULL;
|
||||
sGpuBgConfigs2[bg].bg_x = 0;
|
||||
sGpuBgConfigs2[bg].bg_y = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void SetBgMode(u8 bgMode)
|
||||
{
|
||||
SetBgModeInternal(bgMode);
|
||||
}
|
||||
|
||||
u16 LoadBgTiles(u8 bg, const void* src, u16 size, u16 destOffset)
|
||||
{
|
||||
u16 tileOffset;
|
||||
u8 cursor;
|
||||
|
||||
if (GetBgControlAttribute(bg, BG_CTRL_ATTR_PALETTEMODE) == 0)
|
||||
{
|
||||
tileOffset = (sGpuBgConfigs2[bg].baseTile + destOffset) * 0x20;
|
||||
}
|
||||
else
|
||||
{
|
||||
tileOffset = (sGpuBgConfigs2[bg].baseTile + destOffset) * 0x40;
|
||||
}
|
||||
|
||||
cursor = LoadBgVram(bg, src, size, tileOffset, DISPCNT_MODE_1);
|
||||
|
||||
if (cursor == 0xFF)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
sDmaBusyBitfield[cursor / 0x20] |= (1 << (cursor % 0x20));
|
||||
|
||||
if (gWindowTileAutoAllocEnabled == TRUE)
|
||||
BgTileAllocOp(bg, tileOffset / 0x20, size / 0x20, 1);
|
||||
|
||||
return cursor;
|
||||
}
|
||||
|
||||
u16 LoadBgTilemap(u8 bg, const void *src, u16 size, u16 destOffset)
|
||||
{
|
||||
u8 cursor = LoadBgVram(bg, src, size, destOffset * 2, DISPCNT_MODE_2);
|
||||
|
||||
if (cursor == 0xFF)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
sDmaBusyBitfield[cursor / 0x20] |= (1 << (cursor % 0x20));
|
||||
|
||||
return cursor;
|
||||
}
|
||||
|
||||
u16 Unused_LoadBgPalette(u8 bg, const void *src, u16 size, u16 destOffset)
|
||||
{
|
||||
s8 cursor;
|
||||
|
||||
if (!IsInvalidBg32(bg))
|
||||
{
|
||||
u16 paletteOffset = (sGpuBgConfigs2[bg].basePalette * 0x20) + (destOffset * 2);
|
||||
cursor = RequestDma3Copy(src, (void*)(paletteOffset + BG_PLTT), size, 0);
|
||||
|
||||
if (cursor == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
sDmaBusyBitfield[cursor / 0x20] |= (1 << (cursor % 0x20));
|
||||
|
||||
return (u8)cursor;
|
||||
}
|
||||
|
||||
bool8 IsDma3ManagerBusyWithBgCopy(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 0x80; i++)
|
||||
{
|
||||
u8 div = i / 0x20;
|
||||
u8 mod = i % 0x20;
|
||||
|
||||
if ((sDmaBusyBitfield[div] & (1 << mod)))
|
||||
{
|
||||
s8 reqSpace = CheckForSpaceForDma3Request(i);
|
||||
if (reqSpace == -1)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
sDmaBusyBitfield[div] &= ~(1 << mod);
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void ShowBg(u8 bg)
|
||||
{
|
||||
ShowBgInternal(bg);
|
||||
SyncBgVisibilityAndMode();
|
||||
}
|
||||
|
||||
void HideBg(u8 bg)
|
||||
{
|
||||
HideBgInternal(bg);
|
||||
SyncBgVisibilityAndMode();
|
||||
}
|
||||
|
||||
void SetBgAttribute(u8 bg, u8 attributeId, u8 value)
|
||||
{
|
||||
switch (attributeId)
|
||||
{
|
||||
case BG_ATTR_CHARBASEINDEX:
|
||||
SetBgControlAttributes(bg, value, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF);
|
||||
break;
|
||||
case BG_ATTR_MAPBASEINDEX:
|
||||
SetBgControlAttributes(bg, 0xFF, value, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF);
|
||||
break;
|
||||
case BG_ATTR_SCREENSIZE:
|
||||
SetBgControlAttributes(bg, 0xFF, 0xFF, value, 0xFF, 0xFF, 0xFF, 0xFF);
|
||||
break;
|
||||
case BG_ATTR_PALETTEMODE:
|
||||
SetBgControlAttributes(bg, 0xFF, 0xFF, 0xFF, value, 0xFF, 0xFF, 0xFF);
|
||||
break;
|
||||
case BG_ATTR_PRIORITY:
|
||||
SetBgControlAttributes(bg, 0xFF, 0xFF, 0xFF, 0xFF, value, 0xFF, 0xFF);
|
||||
break;
|
||||
case BG_ATTR_MOSAIC:
|
||||
SetBgControlAttributes(bg, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, value, 0xFF);
|
||||
break;
|
||||
case BG_ATTR_WRAPAROUND:
|
||||
SetBgControlAttributes(bg, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
u16 GetBgAttribute(u8 bg, u8 attributeId)
|
||||
{
|
||||
switch (attributeId)
|
||||
{
|
||||
case BG_ATTR_CHARBASEINDEX:
|
||||
return GetBgControlAttribute(bg, BG_CTRL_ATTR_CHARBASEINDEX);
|
||||
case BG_ATTR_MAPBASEINDEX:
|
||||
return GetBgControlAttribute(bg, BG_CTRL_ATTR_MAPBASEINDEX);
|
||||
case BG_ATTR_SCREENSIZE:
|
||||
return GetBgControlAttribute(bg, BG_CTRL_ATTR_SCREENSIZE);
|
||||
case BG_ATTR_PALETTEMODE:
|
||||
return GetBgControlAttribute(bg, BG_CTRL_ATTR_PALETTEMODE);
|
||||
case BG_ATTR_PRIORITY:
|
||||
return GetBgControlAttribute(bg, BG_CTRL_ATTR_PRIORITY);
|
||||
case BG_ATTR_MOSAIC:
|
||||
return GetBgControlAttribute(bg, BG_CTRL_ATTR_MOSAIC);
|
||||
case BG_ATTR_WRAPAROUND:
|
||||
return GetBgControlAttribute(bg, BG_CTRL_ATTR_WRAPAROUND);
|
||||
case BG_ATTR_METRIC:
|
||||
switch (GetBgType(bg))
|
||||
{
|
||||
case BG_TYPE_NORMAL:
|
||||
return GetBgMetricTextMode(bg, 0) * 0x800;
|
||||
case BG_TYPE_AFFINE:
|
||||
return GetBgMetricAffineMode(bg, 0) * 0x100;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
case BG_ATTR_TYPE:
|
||||
return GetBgType(bg);
|
||||
case BG_ATTR_BASETILE:
|
||||
return sGpuBgConfigs2[bg].baseTile;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
s32 ChangeBgX(u8 bg, s32 value, u8 op)
|
||||
{
|
||||
u8 mode;
|
||||
u16 temp1;
|
||||
u16 temp2;
|
||||
|
||||
if (IsInvalidBg32(bg) || !GetBgControlAttribute(bg, BG_CTRL_ATTR_VISIBLE))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case BG_COORD_SET:
|
||||
default:
|
||||
sGpuBgConfigs2[bg].bg_x = value;
|
||||
break;
|
||||
case BG_COORD_ADD:
|
||||
sGpuBgConfigs2[bg].bg_x += value;
|
||||
break;
|
||||
case BG_COORD_SUB:
|
||||
sGpuBgConfigs2[bg].bg_x -= value;
|
||||
break;
|
||||
}
|
||||
|
||||
mode = GetBgMode();
|
||||
|
||||
switch (bg)
|
||||
{
|
||||
case 0:
|
||||
temp1 = sGpuBgConfigs2[0].bg_x >> 0x8;
|
||||
SetGpuReg(REG_OFFSET_BG0HOFS, temp1);
|
||||
break;
|
||||
case 1:
|
||||
temp1 = sGpuBgConfigs2[1].bg_x >> 0x8;
|
||||
SetGpuReg(REG_OFFSET_BG1HOFS, temp1);
|
||||
break;
|
||||
case 2:
|
||||
if (mode == 0)
|
||||
{
|
||||
temp1 = sGpuBgConfigs2[2].bg_x >> 0x8;
|
||||
SetGpuReg(REG_OFFSET_BG2HOFS, temp1);
|
||||
}
|
||||
else
|
||||
{
|
||||
temp1 = sGpuBgConfigs2[2].bg_x >> 0x10;
|
||||
temp2 = sGpuBgConfigs2[2].bg_x & 0xFFFF;
|
||||
SetGpuReg(REG_OFFSET_BG2X_H, temp1);
|
||||
SetGpuReg(REG_OFFSET_BG2X_L, temp2);
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (mode == 0)
|
||||
{
|
||||
temp1 = sGpuBgConfigs2[3].bg_x >> 0x8;
|
||||
SetGpuReg(REG_OFFSET_BG3HOFS, temp1);
|
||||
}
|
||||
else if (mode == 2)
|
||||
{
|
||||
temp1 = sGpuBgConfigs2[3].bg_x >> 0x10;
|
||||
temp2 = sGpuBgConfigs2[3].bg_x & 0xFFFF;
|
||||
SetGpuReg(REG_OFFSET_BG3X_H, temp1);
|
||||
SetGpuReg(REG_OFFSET_BG3X_L, temp2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return sGpuBgConfigs2[bg].bg_x;
|
||||
}
|
||||
|
||||
s32 GetBgX(u8 bg)
|
||||
{
|
||||
if (IsInvalidBg32(bg))
|
||||
return -1;
|
||||
else if (!GetBgControlAttribute(bg, BG_CTRL_ATTR_VISIBLE))
|
||||
return -1;
|
||||
else
|
||||
return sGpuBgConfigs2[bg].bg_x;
|
||||
}
|
||||
|
||||
s32 ChangeBgY(u8 bg, s32 value, u8 op)
|
||||
{
|
||||
u8 mode;
|
||||
u16 temp1;
|
||||
u16 temp2;
|
||||
|
||||
if (IsInvalidBg32(bg) || !GetBgControlAttribute(bg, BG_CTRL_ATTR_VISIBLE))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case BG_COORD_SET:
|
||||
default:
|
||||
sGpuBgConfigs2[bg].bg_y = value;
|
||||
break;
|
||||
case BG_COORD_ADD:
|
||||
sGpuBgConfigs2[bg].bg_y += value;
|
||||
break;
|
||||
case BG_COORD_SUB:
|
||||
sGpuBgConfigs2[bg].bg_y -= value;
|
||||
break;
|
||||
}
|
||||
|
||||
mode = GetBgMode();
|
||||
|
||||
switch (bg)
|
||||
{
|
||||
case 0:
|
||||
temp1 = sGpuBgConfigs2[0].bg_y >> 0x8;
|
||||
SetGpuReg(REG_OFFSET_BG0VOFS, temp1);
|
||||
break;
|
||||
case 1:
|
||||
temp1 = sGpuBgConfigs2[1].bg_y >> 0x8;
|
||||
SetGpuReg(REG_OFFSET_BG1VOFS, temp1);
|
||||
break;
|
||||
case 2:
|
||||
if (mode == 0)
|
||||
{
|
||||
temp1 = sGpuBgConfigs2[2].bg_y >> 0x8;
|
||||
SetGpuReg(REG_OFFSET_BG2VOFS, temp1);
|
||||
}
|
||||
else
|
||||
{
|
||||
temp1 = sGpuBgConfigs2[2].bg_y >> 0x10;
|
||||
temp2 = sGpuBgConfigs2[2].bg_y & 0xFFFF;
|
||||
SetGpuReg(REG_OFFSET_BG2Y_H, temp1);
|
||||
SetGpuReg(REG_OFFSET_BG2Y_L, temp2);
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (mode == 0)
|
||||
{
|
||||
temp1 = sGpuBgConfigs2[3].bg_y >> 0x8;
|
||||
SetGpuReg(REG_OFFSET_BG3VOFS, temp1);
|
||||
}
|
||||
else if (mode == 2)
|
||||
{
|
||||
temp1 = sGpuBgConfigs2[3].bg_y >> 0x10;
|
||||
temp2 = sGpuBgConfigs2[3].bg_y & 0xFFFF;
|
||||
SetGpuReg(REG_OFFSET_BG3Y_H, temp1);
|
||||
SetGpuReg(REG_OFFSET_BG3Y_L, temp2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return sGpuBgConfigs2[bg].bg_y;
|
||||
}
|
||||
|
||||
s32 ChangeBgY_ScreenOff(u8 bg, s32 value, u8 op)
|
||||
{
|
||||
u8 mode;
|
||||
u16 temp1;
|
||||
u16 temp2;
|
||||
|
||||
if (IsInvalidBg32(bg) || !GetBgControlAttribute(bg, BG_CTRL_ATTR_VISIBLE))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case BG_COORD_SET:
|
||||
default:
|
||||
sGpuBgConfigs2[bg].bg_y = value;
|
||||
break;
|
||||
case BG_COORD_ADD:
|
||||
sGpuBgConfigs2[bg].bg_y += value;
|
||||
break;
|
||||
case BG_COORD_SUB:
|
||||
sGpuBgConfigs2[bg].bg_y -= value;
|
||||
break;
|
||||
}
|
||||
|
||||
mode = GetBgMode();
|
||||
|
||||
switch (bg)
|
||||
{
|
||||
case 0:
|
||||
temp1 = sGpuBgConfigs2[0].bg_y >> 0x8;
|
||||
SetGpuReg_ForcedBlank(REG_OFFSET_BG0VOFS, temp1);
|
||||
break;
|
||||
case 1:
|
||||
temp1 = sGpuBgConfigs2[1].bg_y >> 0x8;
|
||||
SetGpuReg_ForcedBlank(REG_OFFSET_BG1VOFS, temp1);
|
||||
break;
|
||||
case 2:
|
||||
if (mode == 0)
|
||||
{
|
||||
temp1 = sGpuBgConfigs2[2].bg_y >> 0x8;
|
||||
SetGpuReg_ForcedBlank(REG_OFFSET_BG2VOFS, temp1);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
temp1 = sGpuBgConfigs2[2].bg_y >> 0x10;
|
||||
temp2 = sGpuBgConfigs2[2].bg_y & 0xFFFF;
|
||||
SetGpuReg_ForcedBlank(REG_OFFSET_BG2Y_H, temp1);
|
||||
SetGpuReg_ForcedBlank(REG_OFFSET_BG2Y_L, temp2);
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (mode == 0)
|
||||
{
|
||||
temp1 = sGpuBgConfigs2[3].bg_y >> 0x8;
|
||||
SetGpuReg_ForcedBlank(REG_OFFSET_BG3VOFS, temp1);
|
||||
}
|
||||
else if (mode == 2)
|
||||
{
|
||||
temp1 = sGpuBgConfigs2[3].bg_y >> 0x10;
|
||||
temp2 = sGpuBgConfigs2[3].bg_y & 0xFFFF;
|
||||
SetGpuReg_ForcedBlank(REG_OFFSET_BG3Y_H, temp1);
|
||||
SetGpuReg_ForcedBlank(REG_OFFSET_BG3Y_L, temp2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return sGpuBgConfigs2[bg].bg_y;
|
||||
}
|
||||
|
||||
s32 GetBgY(u8 bg)
|
||||
{
|
||||
if (IsInvalidBg32(bg))
|
||||
return -1;
|
||||
else if (!GetBgControlAttribute(bg, BG_CTRL_ATTR_VISIBLE))
|
||||
return -1;
|
||||
else
|
||||
return sGpuBgConfigs2[bg].bg_y;
|
||||
}
|
||||
|
||||
void SetBgAffine(u8 bg, s32 srcCenterX, s32 srcCenterY, s16 dispCenterX, s16 dispCenterY, s16 scaleX, s16 scaleY, u16 rotationAngle)
|
||||
{
|
||||
SetBgAffineInternal(bg, srcCenterX, srcCenterY, dispCenterX, dispCenterY, scaleX, scaleY, rotationAngle);
|
||||
}
|
||||
|
||||
u8 Unused_AdjustBgMosaic(u8 a1, u8 a2)
|
||||
{
|
||||
u16 result = GetGpuReg(REG_OFFSET_MOSAIC);
|
||||
s16 test1 = result & 0xF;
|
||||
s16 test2 = (result >> 4) & 0xF;
|
||||
|
||||
result &= 0xFF00;
|
||||
|
||||
switch (a2)
|
||||
{
|
||||
case 0:
|
||||
default:
|
||||
test1 = a1 & 0xF;
|
||||
test2 = a1 >> 0x4;
|
||||
break;
|
||||
case 1:
|
||||
test1 = a1 & 0xF;
|
||||
break;
|
||||
case 2:
|
||||
if ((test1 + a1) > 0xF)
|
||||
{
|
||||
test1 = 0xF;
|
||||
}
|
||||
else
|
||||
{
|
||||
test1 += a1;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if ((test1 - a1) < 0)
|
||||
{
|
||||
test1 = 0x0;
|
||||
}
|
||||
else
|
||||
{
|
||||
test1 -= a1;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
test2 = a1 & 0xF;
|
||||
break;
|
||||
case 5:
|
||||
if ((test2 + a1) > 0xF)
|
||||
{
|
||||
test2 = 0xF;
|
||||
}
|
||||
else
|
||||
{
|
||||
test2 += a1;
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
if ((test2 - a1) < 0)
|
||||
{
|
||||
test2 = 0x0;
|
||||
}
|
||||
else
|
||||
{
|
||||
test2 -= a1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
result |= ((test2 << 0x4) & 0xF0);
|
||||
result |= (test1 & 0xF);
|
||||
|
||||
SetGpuReg(REG_OFFSET_MOSAIC, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void SetBgTilemapBuffer(u8 bg, void *tilemap)
|
||||
{
|
||||
if (!IsInvalidBg32(bg) && GetBgControlAttribute(bg, BG_CTRL_ATTR_VISIBLE))
|
||||
{
|
||||
sGpuBgConfigs2[bg].tilemap = tilemap;
|
||||
}
|
||||
}
|
||||
|
||||
void UnsetBgTilemapBuffer(u8 bg)
|
||||
{
|
||||
if (!IsInvalidBg32(bg) && GetBgControlAttribute(bg, BG_CTRL_ATTR_VISIBLE))
|
||||
{
|
||||
sGpuBgConfigs2[bg].tilemap = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void* GetBgTilemapBuffer(u8 bg)
|
||||
{
|
||||
if (IsInvalidBg32(bg))
|
||||
return NULL;
|
||||
else if (!GetBgControlAttribute(bg, BG_CTRL_ATTR_VISIBLE))
|
||||
return NULL;
|
||||
else
|
||||
return sGpuBgConfigs2[bg].tilemap;
|
||||
}
|
||||
|
||||
void CopyToBgTilemapBuffer(u8 bg, const void *src, u16 mode, u16 destOffset)
|
||||
{
|
||||
if (!IsInvalidBg32(bg) && !IsTileMapOutsideWram(bg))
|
||||
{
|
||||
if (mode != 0)
|
||||
CpuCopy16(src, (void *)(sGpuBgConfigs2[bg].tilemap + (destOffset * 2)), mode);
|
||||
else
|
||||
LZ77UnCompWram(src, (void *)(sGpuBgConfigs2[bg].tilemap + (destOffset * 2)));
|
||||
}
|
||||
}
|
||||
|
||||
void CopyBgTilemapBufferToVram(u8 bg)
|
||||
{
|
||||
u16 sizeToLoad;
|
||||
|
||||
if (!IsInvalidBg32(bg) && !IsTileMapOutsideWram(bg))
|
||||
{
|
||||
switch (GetBgType(bg))
|
||||
{
|
||||
case BG_TYPE_NORMAL:
|
||||
sizeToLoad = GetBgMetricTextMode(bg, 0) * 0x800;
|
||||
break;
|
||||
case BG_TYPE_AFFINE:
|
||||
sizeToLoad = GetBgMetricAffineMode(bg, 0) * 0x100;
|
||||
break;
|
||||
default:
|
||||
sizeToLoad = 0;
|
||||
break;
|
||||
}
|
||||
LoadBgVram(bg, sGpuBgConfigs2[bg].tilemap, sizeToLoad, 0, 2);
|
||||
}
|
||||
}
|
||||
|
||||
void CopyToBgTilemapBufferRect(u8 bg, const void* src, u8 destX, u8 destY, u8 width, u8 height)
|
||||
{
|
||||
u16 destX16;
|
||||
u16 destY16;
|
||||
u16 mode;
|
||||
|
||||
if (!IsInvalidBg32(bg) && !IsTileMapOutsideWram(bg))
|
||||
{
|
||||
switch (GetBgType(bg))
|
||||
{
|
||||
case BG_TYPE_NORMAL:
|
||||
{
|
||||
const u16 * srcCopy = src;
|
||||
for (destY16 = destY; destY16 < (destY + height); destY16++)
|
||||
{
|
||||
for (destX16 = destX; destX16 < (destX + width); destX16++)
|
||||
{
|
||||
((u16*)sGpuBgConfigs2[bg].tilemap)[((destY16 * 0x20) + destX16)] = *srcCopy++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BG_TYPE_AFFINE:
|
||||
{
|
||||
const u8 * srcCopy = src;
|
||||
mode = GetBgMetricAffineMode(bg, 0x1);
|
||||
for (destY16 = destY; destY16 < (destY + height); destY16++)
|
||||
{
|
||||
for (destX16 = destX; destX16 < (destX + width); destX16++)
|
||||
{
|
||||
((u8*)sGpuBgConfigs2[bg].tilemap)[((destY16 * mode) + destX16)] = *srcCopy++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CopyToBgTilemapBufferRect_ChangePalette(u8 bg, const void *src, u8 destX, u8 destY, u8 rectWidth, u8 rectHeight, u8 palette)
|
||||
{
|
||||
CopyRectToBgTilemapBufferRect(bg, src, 0, 0, rectWidth, rectHeight, destX, destY, rectWidth, rectHeight, palette, 0, 0);
|
||||
}
|
||||
|
||||
void CopyRectToBgTilemapBufferRect(u8 bg, const void *src, u8 srcX, u8 srcY, u8 srcWidth, u8 srcHeight, u8 destX, u8 destY, u8 rectWidth, u8 rectHeight, u8 palette1, s16 tileOffset, s16 palette2)
|
||||
{
|
||||
u16 screenWidth, screenHeight, screenSize;
|
||||
u16 var;
|
||||
const void *srcPtr;
|
||||
u16 i, j;
|
||||
|
||||
if (!IsInvalidBg32(bg) && !IsTileMapOutsideWram(bg))
|
||||
{
|
||||
screenSize = GetBgControlAttribute(bg, BG_CTRL_ATTR_SCREENSIZE);
|
||||
screenWidth = GetBgMetricTextMode(bg, 0x1) * 0x20;
|
||||
screenHeight = GetBgMetricTextMode(bg, 0x2) * 0x20;
|
||||
switch (GetBgType(bg))
|
||||
{
|
||||
case BG_TYPE_NORMAL:
|
||||
srcPtr = src + ((srcY * srcWidth) + srcX) * 2;
|
||||
for (i = destY; i < (destY + rectHeight); i++)
|
||||
{
|
||||
for (j = destX; j < (destX + rectWidth); j++)
|
||||
{
|
||||
u16 index = GetTileMapIndexFromCoords(j, i, screenSize, screenWidth, screenHeight);
|
||||
CopyTileMapEntry(srcPtr, sGpuBgConfigs2[bg].tilemap + (index * 2), palette1, tileOffset, palette2);
|
||||
srcPtr += 2;
|
||||
}
|
||||
srcPtr += (srcWidth - rectWidth) * 2;
|
||||
}
|
||||
break;
|
||||
case BG_TYPE_AFFINE:
|
||||
srcPtr = src + ((srcY * srcWidth) + srcX);
|
||||
var = GetBgMetricAffineMode(bg, 0x1);
|
||||
for (i = destY; i < (destY + rectHeight); i++)
|
||||
{
|
||||
for (j = destX; j < (destX + rectWidth); j++)
|
||||
{
|
||||
*(u8*)(sGpuBgConfigs2[bg].tilemap + ((var * i) + j)) = *(u8*)(srcPtr) + tileOffset;
|
||||
srcPtr++;
|
||||
}
|
||||
srcPtr += (srcWidth - rectWidth);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FillBgTilemapBufferRect_Palette0(u8 bg, u16 tileNum, u8 x, u8 y, u8 width, u8 height)
|
||||
{
|
||||
u16 x16;
|
||||
u16 y16;
|
||||
u16 mode;
|
||||
|
||||
if (!IsInvalidBg32(bg) && !IsTileMapOutsideWram(bg))
|
||||
{
|
||||
switch (GetBgType(bg))
|
||||
{
|
||||
case BG_TYPE_NORMAL:
|
||||
for (y16 = y; y16 < (y + height); y16++)
|
||||
{
|
||||
for (x16 = x; x16 < (x + width); x16++)
|
||||
{
|
||||
((u16*)sGpuBgConfigs2[bg].tilemap)[((y16 * 0x20) + x16)] = tileNum;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case BG_TYPE_AFFINE:
|
||||
mode = GetBgMetricAffineMode(bg, 0x1);
|
||||
for (y16 = y; y16 < (y + height); y16++)
|
||||
{
|
||||
for (x16 = x; x16 < (x + width); x16++)
|
||||
{
|
||||
((u8*)sGpuBgConfigs2[bg].tilemap)[((y16 * mode) + x16)] = tileNum;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FillBgTilemapBufferRect(u8 bg, u16 tileNum, u8 x, u8 y, u8 width, u8 height, u8 palette)
|
||||
{
|
||||
WriteSequenceToBgTilemapBuffer(bg, tileNum, x, y, width, height, palette, 0);
|
||||
}
|
||||
|
||||
void WriteSequenceToBgTilemapBuffer(u8 bg, u16 firstTileNum, u8 x, u8 y, u8 width, u8 height, u8 paletteSlot, s16 tileNumDelta)
|
||||
{
|
||||
u16 mode;
|
||||
u16 mode2;
|
||||
u16 attribute;
|
||||
u16 mode3;
|
||||
u16 x16, y16;
|
||||
|
||||
if (!IsInvalidBg32(bg) && !IsTileMapOutsideWram(bg))
|
||||
{
|
||||
attribute = GetBgControlAttribute(bg, BG_CTRL_ATTR_SCREENSIZE);
|
||||
mode = GetBgMetricTextMode(bg, 0x1) * 0x20;
|
||||
mode2 = GetBgMetricTextMode(bg, 0x2) * 0x20;
|
||||
switch (GetBgType(bg))
|
||||
{
|
||||
case BG_TYPE_NORMAL:
|
||||
for (y16 = y; y16 < (y + height); y16++)
|
||||
{
|
||||
for (x16 = x; x16 < (x + width); x16++)
|
||||
{
|
||||
CopyTileMapEntry(&firstTileNum, &((u16*)sGpuBgConfigs2[bg].tilemap)[(u16)GetTileMapIndexFromCoords(x16, y16, attribute, mode, mode2)], paletteSlot, 0, 0);
|
||||
firstTileNum = (firstTileNum & (METATILE_COLLISION_MASK | METATILE_ELEVATION_MASK)) + ((firstTileNum + tileNumDelta) & METATILE_ID_MASK);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case BG_TYPE_AFFINE:
|
||||
mode3 = GetBgMetricAffineMode(bg, 0x1);
|
||||
for (y16 = y; y16 < (y + height); y16++)
|
||||
{
|
||||
for (x16 = x; x16 < (x + width); x16++)
|
||||
{
|
||||
((u8*)sGpuBgConfigs2[bg].tilemap)[(y16 * mode3) + x16] = firstTileNum;
|
||||
firstTileNum = (firstTileNum & (METATILE_COLLISION_MASK | METATILE_ELEVATION_MASK)) + ((firstTileNum + tileNumDelta) & METATILE_ID_MASK);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u16 GetBgMetricTextMode(u8 bg, u8 whichMetric)
|
||||
{
|
||||
u8 screenSize = GetBgControlAttribute(bg, BG_CTRL_ATTR_SCREENSIZE);
|
||||
|
||||
switch (whichMetric)
|
||||
{
|
||||
case 0:
|
||||
switch (screenSize)
|
||||
{
|
||||
case 0:
|
||||
return 1;
|
||||
case 1:
|
||||
case 2:
|
||||
return 2;
|
||||
case 3:
|
||||
return 4;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
switch (screenSize)
|
||||
{
|
||||
case 0:
|
||||
return 1;
|
||||
case 1:
|
||||
return 2;
|
||||
case 2:
|
||||
return 1;
|
||||
case 3:
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
switch (screenSize)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
return 1;
|
||||
case 2:
|
||||
case 3:
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 GetBgMetricAffineMode(u8 bg, u8 whichMetric)
|
||||
{
|
||||
u8 screenSize = GetBgControlAttribute(bg, BG_CTRL_ATTR_SCREENSIZE);
|
||||
|
||||
switch (whichMetric)
|
||||
{
|
||||
case 0:
|
||||
switch (screenSize)
|
||||
{
|
||||
case 0:
|
||||
return 0x1;
|
||||
case 1:
|
||||
return 0x4;
|
||||
case 2:
|
||||
return 0x10;
|
||||
case 3:
|
||||
return 0x40;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
return 0x10 << screenSize;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 GetTileMapIndexFromCoords(s32 x, s32 y, s32 screenSize, u32 screenWidth, u32 screenHeight)
|
||||
{
|
||||
x = x & (screenWidth - 1);
|
||||
y = y & (screenHeight - 1);
|
||||
|
||||
switch (screenSize)
|
||||
{
|
||||
case 0:
|
||||
case 2:
|
||||
break;
|
||||
case 3:
|
||||
if (y >= 0x20)
|
||||
y += 0x20;
|
||||
case 1:
|
||||
if (x >= 0x20)
|
||||
{
|
||||
x -= 0x20;
|
||||
y += 0x20;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return (y * 0x20) + x;
|
||||
}
|
||||
|
||||
void CopyTileMapEntry(const u16 *src, u16 *dest, s32 palette1, s32 tileOffset, s32 palette2)
|
||||
{
|
||||
u16 var;
|
||||
|
||||
switch (palette1)
|
||||
{
|
||||
case 0 ... 15:
|
||||
var = ((*src + tileOffset) & 0xFFF) + ((palette1 + palette2) << 12);
|
||||
break;
|
||||
case 16:
|
||||
var = *dest;
|
||||
var &= 0xFC00;
|
||||
var += palette2 << 12;
|
||||
var |= (*src + tileOffset) & 0x3FF;
|
||||
break;
|
||||
default:
|
||||
case 17 ... INT_MAX:
|
||||
var = *src + tileOffset + (palette2 << 12);
|
||||
break;
|
||||
}
|
||||
*dest = var;
|
||||
}
|
||||
|
||||
static u32 GetBgType(u8 bg)
|
||||
{
|
||||
u8 mode = GetBgMode();
|
||||
|
||||
switch (bg)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
switch (mode)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
return BG_TYPE_NORMAL;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
switch (mode)
|
||||
{
|
||||
case 0:
|
||||
return BG_TYPE_NORMAL;
|
||||
case 1:
|
||||
case 2:
|
||||
return BG_TYPE_AFFINE;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
switch (mode)
|
||||
{
|
||||
case 0:
|
||||
return BG_TYPE_NORMAL;
|
||||
case 2:
|
||||
return BG_TYPE_AFFINE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return BG_TYPE_NONE;
|
||||
}
|
||||
|
||||
bool32 IsInvalidBg32(u8 bg)
|
||||
{
|
||||
if (bg >= NUM_BACKGROUNDS)
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool32 IsTileMapOutsideWram(u8 bg)
|
||||
{
|
||||
if (sGpuBgConfigs2[bg].tilemap > (void*)IWRAM_END)
|
||||
return TRUE;
|
||||
else if (sGpuBgConfigs2[bg].tilemap == NULL)
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
87
gflib/bg.h
Normal file
87
gflib/bg.h
Normal file
@ -0,0 +1,87 @@
|
||||
#ifndef GUARD_BG_H
|
||||
#define GUARD_BG_H
|
||||
|
||||
enum
|
||||
{
|
||||
BG_ATTR_CHARBASEINDEX = 1,
|
||||
BG_ATTR_MAPBASEINDEX,
|
||||
BG_ATTR_SCREENSIZE,
|
||||
BG_ATTR_PALETTEMODE,
|
||||
BG_ATTR_MOSAIC,
|
||||
BG_ATTR_WRAPAROUND,
|
||||
BG_ATTR_PRIORITY,
|
||||
BG_ATTR_METRIC,
|
||||
BG_ATTR_TYPE,
|
||||
BG_ATTR_BASETILE,
|
||||
};
|
||||
|
||||
enum {
|
||||
BG_TYPE_NORMAL,
|
||||
BG_TYPE_AFFINE,
|
||||
BG_TYPE_NONE = 0xFFFF
|
||||
};
|
||||
|
||||
// Modes for ChangeBgX / ChangeBgY
|
||||
enum {
|
||||
BG_COORD_SET,
|
||||
BG_COORD_ADD,
|
||||
BG_COORD_SUB,
|
||||
};
|
||||
|
||||
struct BgTemplate
|
||||
{
|
||||
u16 bg:2; // 0x1, 0x2 -> 0x3
|
||||
u16 charBaseIndex:2; // 0x4, 0x8 -> 0xC
|
||||
u16 mapBaseIndex:5; // 0x10, 0x20, 0x40, 0x80, 0x100 -> 0x1F0
|
||||
u16 screenSize:2; // 0x200, 0x400 -> 0x600
|
||||
u16 paletteMode:1; // 0x800
|
||||
u16 priority:2; // 0x1000, 0x2000 > 0x3000
|
||||
u16 baseTile:10;
|
||||
};
|
||||
|
||||
void ResetBgs(void);
|
||||
u8 GetBgMode(void);
|
||||
void ResetBgControlStructs(void);
|
||||
void Unused_ResetBgControlStruct(u8 bg);
|
||||
u8 LoadBgVram(u8 bg, const void *src, u16 size, u16 destOffset, u8 mode);
|
||||
void SetTextModeAndHideBgs(void);
|
||||
bool8 IsInvalidBg(u8 bg);
|
||||
int BgTileAllocOp(int bg, int offset, int count, int mode);
|
||||
void ResetBgsAndClearDma3BusyFlags(u32 leftoverFireRedLeafGreenVariable);
|
||||
void InitBgsFromTemplates(u8 bgMode, const struct BgTemplate *templates, u8 numTemplates);
|
||||
void InitBgFromTemplate(const struct BgTemplate *template);
|
||||
void SetBgMode(u8 bgMode);
|
||||
u16 LoadBgTiles(u8 bg, const void* src, u16 size, u16 destOffset);
|
||||
u16 LoadBgTilemap(u8 bg, const void *src, u16 size, u16 destOffset);
|
||||
u16 Unused_LoadBgPalette(u8 bg, const void *src, u16 size, u16 destOffset);
|
||||
bool8 IsDma3ManagerBusyWithBgCopy(void);
|
||||
void ShowBg(u8 bg);
|
||||
void HideBg(u8 bg);
|
||||
void SetBgAttribute(u8 bg, u8 attributeId, u8 value);
|
||||
u16 GetBgAttribute(u8 bg, u8 attributeId);
|
||||
s32 ChangeBgX(u8 bg, s32 value, u8 op);
|
||||
s32 GetBgX(u8 bg);
|
||||
s32 ChangeBgY(u8 bg, s32 value, u8 op);
|
||||
s32 ChangeBgY_ScreenOff(u8 bg, s32 value, u8 op);
|
||||
s32 GetBgY(u8 bg);
|
||||
void SetBgAffine(u8 bg, s32 srcCenterX, s32 srcCenterY, s16 dispCenterX, s16 dispCenterY, s16 scaleX, s16 scaleY, u16 rotationAngle);
|
||||
u8 Unused_AdjustBgMosaic(u8 a1, u8 a2);
|
||||
void SetBgTilemapBuffer(u8 bg, void *tilemap);
|
||||
void UnsetBgTilemapBuffer(u8 bg);
|
||||
void* GetBgTilemapBuffer(u8 bg);
|
||||
void CopyToBgTilemapBuffer(u8 bg, const void *src, u16 mode, u16 destOffset);
|
||||
void CopyBgTilemapBufferToVram(u8 bg);
|
||||
void CopyToBgTilemapBufferRect(u8 bg, const void* src, u8 destX, u8 destY, u8 width, u8 height);
|
||||
void CopyToBgTilemapBufferRect_ChangePalette(u8 bg, const void *src, u8 destX, u8 destY, u8 rectWidth, u8 rectHeight, u8 palette);
|
||||
void CopyRectToBgTilemapBufferRect(u8 bg, const void *src, u8 srcX, u8 srcY, u8 srcWidth, u8 srcHeight, u8 destX, u8 destY, u8 rectWidth, u8 rectHeight, u8 palette1, s16 tileOffset, s16 palette2);
|
||||
void FillBgTilemapBufferRect_Palette0(u8 bg, u16 tileNum, u8 x, u8 y, u8 width, u8 height);
|
||||
void FillBgTilemapBufferRect(u8 bg, u16 tileNum, u8 x, u8 y, u8 width, u8 height, u8 palette);
|
||||
void WriteSequenceToBgTilemapBuffer(u8 bg, u16 firstTileNum, u8 x, u8 y, u8 width, u8 height, u8 paletteSlot, s16 tileNumDelta);
|
||||
u16 GetBgMetricTextMode(u8 bg, u8 whichMetric);
|
||||
u32 GetBgMetricAffineMode(u8 bg, u8 whichMetric);
|
||||
u32 GetTileMapIndexFromCoords(s32 x, s32 y, s32 screenSize, u32 screenWidth, u32 screenHeight);
|
||||
void CopyTileMapEntry(const u16 *src, u16 *dest, s32 palette1, s32 tileOffset, s32 palette2);
|
||||
bool32 IsInvalidBg32(u8 bg);
|
||||
bool32 IsTileMapOutsideWram(u8 bg);
|
||||
|
||||
#endif // GUARD_BG_H
|
209
gflib/blit.c
Normal file
209
gflib/blit.c
Normal file
@ -0,0 +1,209 @@
|
||||
#include "global.h"
|
||||
#include "blit.h"
|
||||
|
||||
void BlitBitmapRect4BitWithoutColorKey(const struct Bitmap *src, struct Bitmap *dst, u16 srcX, u16 srcY, u16 dstX, u16 dstY, u16 width, u16 height)
|
||||
{
|
||||
BlitBitmapRect4Bit(src, dst, srcX, srcY, dstX, dstY, width, height, 0xFF);
|
||||
}
|
||||
|
||||
void BlitBitmapRect4Bit(const struct Bitmap *src, struct Bitmap *dst, u16 srcX, u16 srcY, u16 dstX, u16 dstY, u16 width, u16 height, u8 colorKey)
|
||||
{
|
||||
s32 xEnd;
|
||||
s32 yEnd;
|
||||
s32 multiplierSrcY;
|
||||
s32 multiplierDstY;
|
||||
s32 loopSrcY, loopDstY;
|
||||
s32 loopSrcX, loopDstX;
|
||||
const u8 *pixelsSrc;
|
||||
u8 *pixelsDst;
|
||||
s32 toOrr;
|
||||
s32 toAnd;
|
||||
s32 toShift;
|
||||
|
||||
if (dst->width - dstX < width)
|
||||
xEnd = (dst->width - dstX) + srcX;
|
||||
else
|
||||
xEnd = srcX + width;
|
||||
|
||||
if (dst->height - dstY < height)
|
||||
yEnd = (dst->height - dstY) + srcY;
|
||||
else
|
||||
yEnd = height + srcY;
|
||||
|
||||
multiplierSrcY = (src->width + (src->width & 7)) >> 3;
|
||||
multiplierDstY = (dst->width + (dst->width & 7)) >> 3;
|
||||
|
||||
if (colorKey == 0xFF)
|
||||
{
|
||||
for (loopSrcY = srcY, loopDstY = dstY; loopSrcY < yEnd; loopSrcY++, loopDstY++)
|
||||
{
|
||||
for (loopSrcX = srcX, loopDstX = dstX; loopSrcX < xEnd; loopSrcX++, loopDstX++)
|
||||
{
|
||||
pixelsSrc = src->pixels + ((loopSrcX >> 1) & 3) + ((loopSrcX >> 3) << 5) + (((loopSrcY >> 3) * multiplierSrcY) << 5) + ((u32)(loopSrcY << 0x1d) >> 0x1B);
|
||||
pixelsDst = dst->pixels + ((loopDstX >> 1) & 3) + ((loopDstX >> 3) << 5) + (((loopDstY >> 3) * multiplierDstY) << 5) + ((u32)(loopDstY << 0x1d) >> 0x1B);
|
||||
toOrr = ((*pixelsSrc >> ((loopSrcX & 1) << 2)) & 0xF);
|
||||
toShift = ((loopDstX & 1) << 2);
|
||||
toOrr <<= toShift;
|
||||
toAnd = 0xF0 >> (toShift);
|
||||
*pixelsDst = toOrr | (*pixelsDst & toAnd);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (loopSrcY = srcY, loopDstY = dstY; loopSrcY < yEnd; loopSrcY++, loopDstY++)
|
||||
{
|
||||
for (loopSrcX = srcX, loopDstX = dstX; loopSrcX < xEnd; loopSrcX++, loopDstX++)
|
||||
{
|
||||
pixelsSrc = src->pixels + ((loopSrcX >> 1) & 3) + ((loopSrcX >> 3) << 5) + (((loopSrcY >> 3) * multiplierSrcY) << 5) + ((u32)(loopSrcY << 0x1d) >> 0x1B);
|
||||
pixelsDst = dst->pixels + ((loopDstX >> 1) & 3) + ((loopDstX >> 3) << 5) + (((loopDstY >> 3) * multiplierDstY) << 5) + ((u32)(loopDstY << 0x1d) >> 0x1B);
|
||||
toOrr = ((*pixelsSrc >> ((loopSrcX & 1) << 2)) & 0xF);
|
||||
if (toOrr != colorKey)
|
||||
{
|
||||
toShift = ((loopDstX & 1) << 2);
|
||||
toOrr <<= toShift;
|
||||
toAnd = 0xF0 >> (toShift);
|
||||
*pixelsDst = toOrr | (*pixelsDst & toAnd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FillBitmapRect4Bit(struct Bitmap *surface, u16 x, u16 y, u16 width, u16 height, u8 fillValue)
|
||||
{
|
||||
s32 xEnd;
|
||||
s32 yEnd;
|
||||
s32 multiplierY;
|
||||
s32 loopX, loopY;
|
||||
u8 toOrr1, toOrr2;
|
||||
|
||||
xEnd = x + width;
|
||||
if (xEnd > surface->width)
|
||||
xEnd = surface->width;
|
||||
|
||||
yEnd = y + height;
|
||||
if (yEnd > surface->height)
|
||||
yEnd = surface->height;
|
||||
|
||||
multiplierY = (surface->width + (surface->width & 7)) >> 3;
|
||||
toOrr1 = fillValue << 4;
|
||||
toOrr2 = fillValue & 0xF;
|
||||
|
||||
for (loopY = y; loopY < yEnd; loopY++)
|
||||
{
|
||||
for (loopX = x; loopX < xEnd; loopX++)
|
||||
{
|
||||
u8 *pixels = surface->pixels + ((loopX >> 1) & 3) + ((loopX >> 3) << 5) + (((loopY >> 3) * multiplierY) << 5) + ((u32)(loopY << 0x1d) >> 0x1B);
|
||||
if ((loopX << 0x1F) != 0)
|
||||
*pixels = toOrr1 | (*pixels & 0xF);
|
||||
else
|
||||
*pixels = toOrr2 | (*pixels & 0xF0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BlitBitmapRect4BitTo8Bit(const struct Bitmap *src, struct Bitmap *dst, u16 srcX, u16 srcY, u16 dstX, u16 dstY, u16 width, u16 height, u8 colorKey, u8 paletteOffset)
|
||||
{
|
||||
s32 palOffsetBits;
|
||||
s32 xEnd;
|
||||
s32 yEnd;
|
||||
s32 multiplierSrcY;
|
||||
s32 multiplierDstY;
|
||||
s32 loopSrcY, loopDstY;
|
||||
s32 loopSrcX, loopDstX;
|
||||
const u8 *pixelsSrc;
|
||||
u8 *pixelsDst;
|
||||
s32 colorKeyBits;
|
||||
|
||||
palOffsetBits = (u32)(paletteOffset << 0x1C) >> 0x18;
|
||||
colorKeyBits = (u32)(colorKey << 0x1C) >> 0x18;
|
||||
|
||||
if (dst->width - dstX < width)
|
||||
xEnd = (dst->width - dstX) + srcX;
|
||||
else
|
||||
xEnd = width + srcX;
|
||||
|
||||
if (dst->height - dstY < height)
|
||||
yEnd = (srcY + dst->height) - dstY;
|
||||
else
|
||||
yEnd = srcY + height;
|
||||
|
||||
multiplierSrcY = (src->width + (src->width & 7)) >> 3;
|
||||
multiplierDstY = (dst->width + (dst->width & 7)) >> 3;
|
||||
|
||||
if (colorKey == 0xFF)
|
||||
{
|
||||
for (loopSrcY = srcY, loopDstY = dstY; loopSrcY < yEnd; loopSrcY++, loopDstY++)
|
||||
{
|
||||
pixelsSrc = src->pixels + ((srcX >> 1) & 3) + ((srcX >> 3) << 5) + (((loopSrcY >> 3) * multiplierSrcY) << 5) + ((u32)(loopSrcY << 0x1d) >> 0x1b);
|
||||
for (loopSrcX = srcX, loopDstX = dstX; loopSrcX < xEnd; loopSrcX++, loopDstX++)
|
||||
{
|
||||
pixelsDst = dst->pixels + (loopDstX & 7) + ((loopDstX >> 3) << 6) + (((loopDstY >> 3) * multiplierDstY) << 6) + ((u32)(loopDstY << 0x1d) >> 0x1a);
|
||||
if (loopSrcX & 1)
|
||||
{
|
||||
*pixelsDst = palOffsetBits + (*pixelsSrc >> 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
pixelsSrc = src->pixels + ((loopSrcX >> 1) & 3) + ((loopSrcX >> 3) << 5) + (((loopSrcY >> 3) * multiplierSrcY) << 5) + ((u32)(loopSrcY << 0x1d) >> 0x1b);
|
||||
*pixelsDst = palOffsetBits + (*pixelsSrc & 0xF);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (loopSrcY = srcY, loopDstY = dstY; loopSrcY < yEnd; loopSrcY++, loopDstY++)
|
||||
{
|
||||
pixelsSrc = src->pixels + ((srcX >> 1) & 3) + ((srcX >> 3) << 5) + (((loopSrcY >> 3) * multiplierSrcY) << 5) + ((u32)(loopSrcY << 0x1d) >> 0x1b);
|
||||
for (loopSrcX = srcX, loopDstX = dstX; loopSrcX < xEnd; loopSrcX++, loopDstX++)
|
||||
{
|
||||
if (loopSrcX & 1)
|
||||
{
|
||||
if ((*pixelsSrc & 0xF0) != colorKeyBits)
|
||||
{
|
||||
pixelsDst = dst->pixels + (loopDstX & 7) + ((loopDstX >> 3) << 6) + (((loopDstY >> 3) * multiplierDstY) << 6) + ((u32)(loopDstY << 0x1d) >> 0x1a);
|
||||
*pixelsDst = palOffsetBits + (*pixelsSrc >> 4);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pixelsSrc = src->pixels + ((loopSrcX >> 1) & 3) + ((loopSrcX >> 3) << 5) + (((loopSrcY >> 3) * multiplierSrcY) << 5) + ((u32)(loopSrcY << 0x1d) >> 0x1b);
|
||||
if ((*pixelsSrc & 0xF) != colorKey)
|
||||
{
|
||||
pixelsDst = dst->pixels + (loopDstX & 7) + ((loopDstX >> 3) << 6) + (((loopDstY >> 3) * multiplierDstY) << 6) + ((u32)(loopDstY << 0x1d) >> 0x1a);
|
||||
*pixelsDst = palOffsetBits + (*pixelsSrc & 0xF);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FillBitmapRect8Bit(struct Bitmap *surface, u16 x, u16 y, u16 width, u16 height, u8 fillValue)
|
||||
{
|
||||
s32 xEnd;
|
||||
s32 yEnd;
|
||||
s32 multiplierY;
|
||||
s32 loopX, loopY;
|
||||
|
||||
xEnd = x + width;
|
||||
if (xEnd > surface->width)
|
||||
xEnd = surface->width;
|
||||
|
||||
yEnd = y + height;
|
||||
if (yEnd > surface->height)
|
||||
yEnd = surface->height;
|
||||
|
||||
multiplierY = (surface->width + (surface->width & 7)) >> 3;
|
||||
|
||||
for (loopY = y; loopY < yEnd; loopY++)
|
||||
{
|
||||
for (loopX = x; loopX < xEnd; loopX++)
|
||||
{
|
||||
u8 *pixels = surface->pixels + (loopX & 7) + ((loopX >> 3) << 6) + (((loopY >> 3) * multiplierY) << 6) + ((u32)(loopY << 0x1d) >> 0x1a);
|
||||
*pixels = fillValue;
|
||||
}
|
||||
}
|
||||
}
|
17
gflib/blit.h
Normal file
17
gflib/blit.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef GUARD_BLIT_H
|
||||
#define GUARD_BLIT_H
|
||||
|
||||
struct Bitmap
|
||||
{
|
||||
u8 *pixels;
|
||||
u32 width:16;
|
||||
u32 height:16;
|
||||
};
|
||||
|
||||
void BlitBitmapRect4BitWithoutColorKey(const struct Bitmap *src, struct Bitmap *dst, u16 srcX, u16 srcY, u16 dstX, u16 dstY, u16 width, u16 height);
|
||||
void BlitBitmapRect4Bit(const struct Bitmap *src, struct Bitmap *dst, u16 srcX, u16 srcY, u16 dstX, u16 dstY, u16 width, u16 height, u8 colorKey);
|
||||
void FillBitmapRect4Bit(struct Bitmap *surface, u16 x, u16 y, u16 width, u16 height, u8 fillValue);
|
||||
void BlitBitmapRect4BitTo8Bit(const struct Bitmap *src, struct Bitmap *dst, u16 srcX, u16 srcY, u16 dstX, u16 dstY, u16 width, u16 height, u8 colorKey, u8 paletteOffset);
|
||||
void FillBitmapRect8Bit(struct Bitmap *surface, u16 x, u16 y, u16 width, u16 height, u8 fillValue);
|
||||
|
||||
#endif // GUARD_BLIT_H
|
339
gflib/characters.h
Normal file
339
gflib/characters.h
Normal file
@ -0,0 +1,339 @@
|
||||
#ifndef GUARD_CHARACTERS_H
|
||||
#define GUARD_CHARACTERS_H
|
||||
|
||||
#define CHAR_SPACE 0x00
|
||||
#define CHAR_A_GRAVE 0x01
|
||||
#define CHAR_A_ACUTE 0x02
|
||||
#define CHAR_A_CIRCUMFLEX 0x03
|
||||
#define CHAR_C_CEDILLA 0x04
|
||||
#define CHAR_E_GRAVE 0x05
|
||||
#define CHAR_E_ACUTE 0x06
|
||||
#define CHAR_E_CIRCUMFLEX 0x07
|
||||
#define CHAR_E_DIAERESIS 0x08
|
||||
#define CHAR_I_GRAVE 0x09
|
||||
//#define CHAR_I_ACUTE 0x0A // Is 0x5A instead
|
||||
#define CHAR_I_CIRCUMFLEX 0x0B
|
||||
#define CHAR_I_DIAERESIS 0x0C
|
||||
#define CHAR_O_GRAVE 0x0D
|
||||
#define CHAR_O_ACUTE 0x0E
|
||||
#define CHAR_O_CIRCUMFLEX 0x0F
|
||||
#define CHAR_OE 0x10
|
||||
#define CHAR_U_GRAVE 0x11
|
||||
#define CHAR_U_ACUTE 0x12
|
||||
#define CHAR_U_CIRCUMFLEX 0x13
|
||||
#define CHAR_N_TILDE 0x14
|
||||
#define CHAR_ESZETT 0x15
|
||||
#define CHAR_a_GRAVE 0x16
|
||||
#define CHAR_a_ACUTE 0x17
|
||||
//#define CHAR_a_CIRCUMFLEX 0x18 // Is 0x68 instead
|
||||
#define CHAR_c_CEDILLA 0x19
|
||||
#define CHAR_e_GRAVE 0x1A
|
||||
#define CHAR_e_ACUTE 0x1B
|
||||
#define CHAR_e_CIRCUMFLEX 0x1C
|
||||
#define CHAR_e_DIAERESIS 0x1D
|
||||
#define CHAR_i_GRAVE 0x1E
|
||||
//#define CHAR_i_ACUTE 0x1F // Is 0x6F instead
|
||||
#define CHAR_i_CIRCUMFLEX 0x20
|
||||
#define CHAR_i_DIAERESIS 0x21
|
||||
#define CHAR_o_GRAVE 0x22
|
||||
#define CHAR_o_ACUTE 0x23
|
||||
#define CHAR_o_CIRCUMFLEX 0x24
|
||||
#define CHAR_oe 0x25
|
||||
#define CHAR_u_GRAVE 0x26
|
||||
#define CHAR_u_ACUTE 0x27
|
||||
#define CHAR_u_CIRCUMFLEX 0x28
|
||||
#define CHAR_n_TILDE 0x29
|
||||
#define CHAR_MASCULINE_ORDINAL 0x2A
|
||||
#define CHAR_FEMININE_ORDINAL 0x2B
|
||||
#define CHAR_SUPER_ER 0x2C
|
||||
#define CHAR_AMPERSAND 0x2D
|
||||
#define CHAR_PLUS 0x2E
|
||||
//
|
||||
#define CHAR_LV 0x34
|
||||
#define CHAR_EQUALS 0x35
|
||||
#define CHAR_SEMICOLON 0x36
|
||||
#define CHAR_BARD_WORD_DELIMIT 0x37 // Empty space to separate words in Bard's song
|
||||
#define CHAR_INV_QUESTION_MARK 0x51
|
||||
#define CHAR_INV_EXCL_MARK 0x52
|
||||
#define CHAR_PK 0x53
|
||||
#define CHAR_MN 0x54
|
||||
#define CHAR_PO 0x55
|
||||
#define CHAR_KE 0x56
|
||||
#define CHAR_BLOCK_1 0x57 // Each of these 3
|
||||
#define CHAR_BLOCK_2 0x58 // chars contains 1/3
|
||||
#define CHAR_BLOCK_3 0x59 // of the word BLOCK
|
||||
#define CHAR_I_ACUTE 0x5A
|
||||
#define CHAR_PERCENT 0x5B
|
||||
#define CHAR_LEFT_PAREN 0x5C
|
||||
#define CHAR_RIGHT_PAREN 0x5D
|
||||
//
|
||||
#define CHAR_a_CIRCUMFLEX 0x68
|
||||
//
|
||||
#define CHAR_i_ACUTE 0x6F
|
||||
//
|
||||
#define CHAR_SPACER 0x77 // Empty space
|
||||
//
|
||||
#define CHAR_UP_ARROW 0x79
|
||||
#define CHAR_DOWN_ARROW 0x7A
|
||||
#define CHAR_LEFT_ARROW 0x7B
|
||||
#define CHAR_RIGHT_ARROW 0x7C
|
||||
//
|
||||
#define CHAR_SUPER_E 0x84
|
||||
#define CHAR_LESS_THAN 0x85
|
||||
#define CHAR_GREATER_THAN 0x86
|
||||
//
|
||||
#define CHAR_SUPER_RE 0xA0
|
||||
#define CHAR_0 0xA1
|
||||
#define CHAR_1 0xA2
|
||||
#define CHAR_2 0xA3
|
||||
#define CHAR_3 0xA4
|
||||
#define CHAR_4 0xA5
|
||||
#define CHAR_5 0xA6
|
||||
#define CHAR_6 0xA7
|
||||
#define CHAR_7 0xA8
|
||||
#define CHAR_8 0xA9
|
||||
#define CHAR_9 0xAA
|
||||
#define CHAR_EXCL_MARK 0xAB
|
||||
#define CHAR_QUESTION_MARK 0xAC
|
||||
#define CHAR_PERIOD 0xAD
|
||||
#define CHAR_HYPHEN 0xAE
|
||||
#define CHAR_BULLET 0xAF
|
||||
#define CHAR_ELLIPSIS 0xB0
|
||||
#define CHAR_DBL_QUOTE_LEFT 0xB1
|
||||
#define CHAR_DBL_QUOTE_RIGHT 0xB2
|
||||
#define CHAR_SGL_QUOTE_LEFT 0xB3
|
||||
#define CHAR_SGL_QUOTE_RIGHT 0xB4
|
||||
#define CHAR_MALE 0xB5
|
||||
#define CHAR_FEMALE 0xB6
|
||||
#define CHAR_CURRENCY 0xB7
|
||||
#define CHAR_COMMA 0xB8
|
||||
#define CHAR_MULT_SIGN 0xB9
|
||||
#define CHAR_SLASH 0xBA
|
||||
#define CHAR_A 0xBB
|
||||
#define CHAR_B 0xBC
|
||||
#define CHAR_C 0xBD
|
||||
#define CHAR_D 0xBE
|
||||
#define CHAR_E 0xBF
|
||||
#define CHAR_F 0xC0
|
||||
#define CHAR_G 0xC1
|
||||
#define CHAR_H 0xC2
|
||||
#define CHAR_I 0xC3
|
||||
#define CHAR_J 0xC4
|
||||
#define CHAR_K 0xC5
|
||||
#define CHAR_L 0xC6
|
||||
#define CHAR_M 0xC7
|
||||
#define CHAR_N 0xC8
|
||||
#define CHAR_O 0xC9
|
||||
#define CHAR_P 0xCA
|
||||
#define CHAR_Q 0xCB
|
||||
#define CHAR_R 0xCC
|
||||
#define CHAR_S 0xCD
|
||||
#define CHAR_T 0xCE
|
||||
#define CHAR_U 0xCF
|
||||
#define CHAR_V 0xD0
|
||||
#define CHAR_W 0xD1
|
||||
#define CHAR_X 0xD2
|
||||
#define CHAR_Y 0xD3
|
||||
#define CHAR_Z 0xD4
|
||||
#define CHAR_a 0xD5
|
||||
#define CHAR_b 0xD6
|
||||
#define CHAR_c 0xD7
|
||||
#define CHAR_d 0xD8
|
||||
#define CHAR_e 0xD9
|
||||
#define CHAR_f 0xDA
|
||||
#define CHAR_g 0xDB
|
||||
#define CHAR_h 0xDC
|
||||
#define CHAR_i 0xDD
|
||||
#define CHAR_j 0xDE
|
||||
#define CHAR_k 0xDF
|
||||
#define CHAR_l 0xE0
|
||||
#define CHAR_m 0xE1
|
||||
#define CHAR_n 0xE2
|
||||
#define CHAR_o 0xE3
|
||||
#define CHAR_p 0xE4
|
||||
#define CHAR_q 0xE5
|
||||
#define CHAR_r 0xE6
|
||||
#define CHAR_s 0xE7
|
||||
#define CHAR_t 0xE8
|
||||
#define CHAR_u 0xE9
|
||||
#define CHAR_v 0xEA
|
||||
#define CHAR_w 0xEB
|
||||
#define CHAR_x 0xEC
|
||||
#define CHAR_y 0xED
|
||||
#define CHAR_z 0xEE
|
||||
#define CHAR_BLACK_TRIANGLE 0xEF
|
||||
#define CHAR_COLON 0xF0
|
||||
#define CHAR_A_DIAERESIS 0xF1
|
||||
#define CHAR_O_DIAERESIS 0xF2
|
||||
#define CHAR_U_DIAERESIS 0xF3
|
||||
#define CHAR_a_DIAERESIS 0xF4
|
||||
#define CHAR_o_DIAERESIS 0xF5
|
||||
#define CHAR_u_DIAERESIS 0xF6
|
||||
#define CHAR_DYNAMIC 0xF7
|
||||
#define CHAR_KEYPAD_ICON 0xF8
|
||||
#define CHAR_EXTRA_SYMBOL 0xF9
|
||||
#define CHAR_PROMPT_SCROLL 0xFA // waits for button press and scrolls dialog
|
||||
#define CHAR_PROMPT_CLEAR 0xFB // waits for button press and clears dialog
|
||||
#define EXT_CTRL_CODE_BEGIN 0xFC // extended control code
|
||||
#define PLACEHOLDER_BEGIN 0xFD // string placeholder
|
||||
#define CHAR_NEWLINE 0xFE
|
||||
#define EOS 0xFF // end of string
|
||||
|
||||
// CHAR_KEYPAD_ICON chars
|
||||
#define CHAR_A_BUTTON 0x00
|
||||
#define CHAR_B_BUTTON 0x01
|
||||
#define CHAR_L_BUTTON 0x02
|
||||
#define CHAR_R_BUTTON 0x03
|
||||
#define CHAR_START_BUTTON 0x04
|
||||
#define CHAR_SELECT_BUTTON 0x05
|
||||
#define CHAR_DPAD_UP 0x06
|
||||
#define CHAR_DPAD_DOWN 0x07
|
||||
#define CHAR_DPAD_LEFT 0x08
|
||||
#define CHAR_DPAD_RIGHT 0x09
|
||||
#define CHAR_DPAD_UPDOWN 0x0A
|
||||
#define CHAR_DPAD_LEFTRIGHT 0x0B
|
||||
#define CHAR_DPAD_NONE 0x0C
|
||||
|
||||
// CHAR_EXTRA_SYMBOL chars
|
||||
#define CHAR_UP_ARROW_2 0x00
|
||||
#define CHAR_DOWN_ARROW_2 0x01
|
||||
#define CHAR_LEFT_ARROW_2 0x02
|
||||
#define CHAR_RIGHT_ARROW_2 0x03
|
||||
#define CHAR_PLUS_2 0x04
|
||||
#define CHAR_LV_2 0x05
|
||||
#define CHAR_PP 0x06
|
||||
#define CHAR_ID 0x07
|
||||
#define CHAR_NO 0x08
|
||||
#define CHAR_UNDERSCORE 0x09
|
||||
|
||||
#define EXT_CTRL_CODE_COLOR 0x01
|
||||
#define EXT_CTRL_CODE_HIGHLIGHT 0x02
|
||||
#define EXT_CTRL_CODE_SHADOW 0x03
|
||||
#define EXT_CTRL_CODE_COLOR_HIGHLIGHT_SHADOW 0x04
|
||||
#define EXT_CTRL_CODE_PALETTE 0x05
|
||||
#define EXT_CTRL_CODE_FONT 0x06
|
||||
#define EXT_CTRL_CODE_RESET_SIZE 0x07
|
||||
#define EXT_CTRL_CODE_PAUSE 0x08
|
||||
#define EXT_CTRL_CODE_PAUSE_UNTIL_PRESS 0x09
|
||||
#define EXT_CTRL_CODE_WAIT_SE 0x0A
|
||||
#define EXT_CTRL_CODE_PLAY_BGM 0x0B
|
||||
#define EXT_CTRL_CODE_ESCAPE 0x0C
|
||||
#define EXT_CTRL_CODE_SHIFT_TEXT 0x0D
|
||||
#define EXT_CTRL_CODE_SHIFT_DOWN 0x0E
|
||||
#define EXT_CTRL_CODE_FILL_WINDOW 0x0F
|
||||
#define EXT_CTRL_CODE_PLAY_SE 0x10
|
||||
#define EXT_CTRL_CODE_CLEAR 0x11
|
||||
#define EXT_CTRL_CODE_SKIP 0x12
|
||||
#define EXT_CTRL_CODE_CLEAR_TO 0x13
|
||||
#define EXT_CTRL_CODE_MIN_LETTER_SPACING 0x14
|
||||
#define EXT_CTRL_CODE_JPN 0x15
|
||||
#define EXT_CTRL_CODE_ENG 0x16
|
||||
#define EXT_CTRL_CODE_PAUSE_MUSIC 0x17
|
||||
#define EXT_CTRL_CODE_RESUME_MUSIC 0x18
|
||||
|
||||
#define TEXT_COLOR_TRANSPARENT 0x0
|
||||
#define TEXT_COLOR_WHITE 0x1
|
||||
#define TEXT_COLOR_DARK_GRAY 0x2
|
||||
#define TEXT_COLOR_LIGHT_GRAY 0x3
|
||||
#define TEXT_COLOR_RED 0x4
|
||||
#define TEXT_COLOR_LIGHT_RED 0x5
|
||||
#define TEXT_COLOR_GREEN 0x6
|
||||
#define TEXT_COLOR_LIGHT_GREEN 0x7
|
||||
#define TEXT_COLOR_BLUE 0x8
|
||||
#define TEXT_COLOR_LIGHT_BLUE 0x9
|
||||
#define TEXT_DYNAMIC_COLOR_1 0xA // Usually white
|
||||
#define TEXT_DYNAMIC_COLOR_2 0xB // Usually white w/ tinge of green
|
||||
#define TEXT_DYNAMIC_COLOR_3 0xC // Usually white
|
||||
#define TEXT_DYNAMIC_COLOR_4 0xD // Usually aquamarine
|
||||
#define TEXT_DYNAMIC_COLOR_5 0xE // Usually blue-green
|
||||
#define TEXT_DYNAMIC_COLOR_6 0xF // Usually cerulean
|
||||
|
||||
#define PLACEHOLDER_ID_UNKNOWN 0x0
|
||||
#define PLACEHOLDER_ID_PLAYER 0x1
|
||||
#define PLACEHOLDER_ID_STRING_VAR_1 0x2
|
||||
#define PLACEHOLDER_ID_STRING_VAR_2 0x3
|
||||
#define PLACEHOLDER_ID_STRING_VAR_3 0x4
|
||||
#define PLACEHOLDER_ID_KUN 0x5
|
||||
#define PLACEHOLDER_ID_RIVAL 0x6
|
||||
#define PLACEHOLDER_ID_VERSION 0x7
|
||||
#define PLACEHOLDER_ID_AQUA 0x8
|
||||
#define PLACEHOLDER_ID_MAGMA 0x9
|
||||
#define PLACEHOLDER_ID_ARCHIE 0xA
|
||||
#define PLACEHOLDER_ID_MAXIE 0xB
|
||||
#define PLACEHOLDER_ID_KYOGRE 0xC
|
||||
#define PLACEHOLDER_ID_GROUDON 0xD
|
||||
|
||||
// battle placeholders are located in battle_message.h
|
||||
|
||||
// Hiragana from 0x1-0x50, Katakana from 0x51-0xA0.
|
||||
// This excludes Japanese punctuation, which end at 0xB0
|
||||
#define JAPANESE_CHAR_END 0xA0
|
||||
|
||||
// Note that while all dot combinations are represented in
|
||||
// the Braille font, they are not all meaningful characters.
|
||||
// Only those that have direct single-character translations are listed.
|
||||
#define BRAILLE_CHAR_SPACE 0x00
|
||||
#define BRAILLE_CHAR_A 0x01
|
||||
//
|
||||
#define BRAILLE_CHAR_C 0x03
|
||||
#define BRAILLE_CHAR_COMMA 0x04
|
||||
#define BRAILLE_CHAR_B 0x05
|
||||
#define BRAILLE_CHAR_I 0x06
|
||||
#define BRAILLE_CHAR_F 0x07
|
||||
//
|
||||
#define BRAILLE_CHAR_E 0x09
|
||||
//
|
||||
#define BRAILLE_CHAR_D 0x0B
|
||||
#define BRAILLE_CHAR_COLON 0x0C
|
||||
#define BRAILLE_CHAR_H 0x0D
|
||||
#define BRAILLE_CHAR_J 0x0E
|
||||
#define BRAILLE_CHAR_G 0x0F
|
||||
#define BRAILLE_CHAR_APOSTROPHE 0x10
|
||||
#define BRAILLE_CHAR_K 0x11
|
||||
#define BRAILLE_CHAR_SLASH 0x12
|
||||
#define BRAILLE_CHAR_M 0x13
|
||||
#define BRAILLE_CHAR_SEMICOLON 0x14
|
||||
#define BRAILLE_CHAR_L 0x15
|
||||
#define BRAILLE_CHAR_S 0x16
|
||||
#define BRAILLE_CHAR_P 0x17
|
||||
//
|
||||
#define BRAILLE_CHAR_O 0x19
|
||||
//
|
||||
#define BRAILLE_CHAR_N 0x1B
|
||||
#define BRAILLE_CHAR_EXCL_MARK 0x1C
|
||||
#define BRAILLE_CHAR_R 0x1D
|
||||
#define BRAILLE_CHAR_T 0x1E
|
||||
#define BRAILLE_CHAR_Q 0x1F
|
||||
//
|
||||
#define BRAILLE_CHAR_PERIOD 0x2C
|
||||
//
|
||||
#define BRAILLE_CHAR_W 0x2E
|
||||
//
|
||||
#define BRAILLE_CHAR_HYPHEN 0x30
|
||||
#define BRAILLE_CHAR_U 0x31
|
||||
//
|
||||
#define BRAILLE_CHAR_X 0x33
|
||||
#define BRAILLE_CHAR_QUESTION_MARK 0x34 // Also double quote left
|
||||
#define BRAILLE_CHAR_V 0x35
|
||||
//
|
||||
#define BRAILLE_CHAR_DBL_QUOTE_RIGHT 0x38
|
||||
#define BRAILLE_CHAR_Z 0x39
|
||||
#define BRAILLE_CHAR_NUMBER 0x3A
|
||||
#define BRAILLE_CHAR_Y 0x3B
|
||||
#define BRAILLE_CHAR_PAREN 0x3C
|
||||
//
|
||||
#define NUM_BRAILLE_CHARS 0x40
|
||||
|
||||
// Digits must be preceded by BRAILLE_CHAR_NUMBER
|
||||
#define BRAILLE_CHAR_1 BRAILLE_CHAR_A
|
||||
#define BRAILLE_CHAR_2 BRAILLE_CHAR_B
|
||||
#define BRAILLE_CHAR_3 BRAILLE_CHAR_C
|
||||
#define BRAILLE_CHAR_4 BRAILLE_CHAR_D
|
||||
#define BRAILLE_CHAR_5 BRAILLE_CHAR_E
|
||||
#define BRAILLE_CHAR_6 BRAILLE_CHAR_F
|
||||
#define BRAILLE_CHAR_7 BRAILLE_CHAR_G
|
||||
#define BRAILLE_CHAR_8 BRAILLE_CHAR_H
|
||||
#define BRAILLE_CHAR_9 BRAILLE_CHAR_I
|
||||
#define BRAILLE_CHAR_0 BRAILLE_CHAR_J
|
||||
|
||||
#endif // GUARD_CHARACTERS_H
|
55
gflib/dma3.h
Normal file
55
gflib/dma3.h
Normal file
@ -0,0 +1,55 @@
|
||||
#ifndef GUARD_DMA3_H
|
||||
#define GUARD_DMA3_H
|
||||
|
||||
// Maximum amount of data we will transfer in one operation
|
||||
#define MAX_DMA_BLOCK_SIZE 0x1000
|
||||
|
||||
#define Dma3CopyLarge_(src, dest, size, bit) \
|
||||
{ \
|
||||
const void *_src = src; \
|
||||
void *_dest = dest; \
|
||||
u32 _size = size; \
|
||||
while (1) \
|
||||
{ \
|
||||
if (_size <= MAX_DMA_BLOCK_SIZE) \
|
||||
{ \
|
||||
DmaCopy##bit(3, _src, _dest, _size); \
|
||||
break; \
|
||||
} \
|
||||
DmaCopy##bit(3, _src, _dest, MAX_DMA_BLOCK_SIZE); \
|
||||
_src += MAX_DMA_BLOCK_SIZE; \
|
||||
_dest += MAX_DMA_BLOCK_SIZE; \
|
||||
_size -= MAX_DMA_BLOCK_SIZE; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define Dma3CopyLarge16_(src, dest, size) Dma3CopyLarge_(src, dest, size, 16)
|
||||
#define Dma3CopyLarge32_(src, dest, size) Dma3CopyLarge_(src, dest, size, 32)
|
||||
|
||||
#define Dma3FillLarge_(value, dest, size, bit) \
|
||||
{ \
|
||||
void *_dest = dest; \
|
||||
u32 _size = size; \
|
||||
while (1) \
|
||||
{ \
|
||||
if (_size <= MAX_DMA_BLOCK_SIZE) \
|
||||
{ \
|
||||
DmaFill##bit(3, value, _dest, _size); \
|
||||
break; \
|
||||
} \
|
||||
DmaFill##bit(3, value, _dest, MAX_DMA_BLOCK_SIZE); \
|
||||
_dest += MAX_DMA_BLOCK_SIZE; \
|
||||
_size -= MAX_DMA_BLOCK_SIZE; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define Dma3FillLarge16_(value, dest, size) Dma3FillLarge_(value, dest, size, 16)
|
||||
#define Dma3FillLarge32_(value, dest, size) Dma3FillLarge_(value, dest, size, 32)
|
||||
|
||||
void ClearDma3Requests(void);
|
||||
void ProcessDma3Requests(void);
|
||||
s16 RequestDma3Copy(const void *src, void *dest, u16 size, u8 mode);
|
||||
s16 RequestDma3Fill(s32 value, void *dest, u16 size, u8 mode);
|
||||
s16 CheckForSpaceForDma3Request(s16 index);
|
||||
|
||||
#endif // GUARD_DMA3_H
|
183
gflib/dma3_manager.c
Normal file
183
gflib/dma3_manager.c
Normal file
@ -0,0 +1,183 @@
|
||||
#include "global.h"
|
||||
#include "dma3.h"
|
||||
|
||||
#define MAX_DMA_REQUESTS 128
|
||||
|
||||
#define DMA_REQUEST_COPY32 1
|
||||
#define DMA_REQUEST_FILL32 2
|
||||
#define DMA_REQUEST_COPY16 3
|
||||
#define DMA_REQUEST_FILL16 4
|
||||
|
||||
struct Dma3Request
|
||||
{
|
||||
const u8 *src;
|
||||
u8 *dest;
|
||||
u16 size;
|
||||
u16 mode;
|
||||
u32 value;
|
||||
};
|
||||
|
||||
static struct Dma3Request sDma3Requests[MAX_DMA_REQUESTS];
|
||||
|
||||
static vbool8 sDma3ManagerLocked;
|
||||
static u8 sDma3RequestCursor;
|
||||
|
||||
void ClearDma3Requests(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
sDma3ManagerLocked = TRUE;
|
||||
sDma3RequestCursor = 0;
|
||||
|
||||
for (i = 0; i < MAX_DMA_REQUESTS; i++)
|
||||
{
|
||||
sDma3Requests[i].size = 0;
|
||||
sDma3Requests[i].src = NULL;
|
||||
sDma3Requests[i].dest = NULL;
|
||||
}
|
||||
|
||||
sDma3ManagerLocked = FALSE;
|
||||
}
|
||||
|
||||
void ProcessDma3Requests(void)
|
||||
{
|
||||
u16 bytesTransferred;
|
||||
|
||||
if (sDma3ManagerLocked)
|
||||
return;
|
||||
|
||||
bytesTransferred = 0;
|
||||
|
||||
// as long as there are DMA requests to process (unless size or vblank is an issue), do not exit
|
||||
while (sDma3Requests[sDma3RequestCursor].size != 0)
|
||||
{
|
||||
bytesTransferred += sDma3Requests[sDma3RequestCursor].size;
|
||||
|
||||
if (bytesTransferred > 40 * 1024)
|
||||
return; // don't transfer more than 40 KiB
|
||||
if (*(u8 *)REG_ADDR_VCOUNT > 224)
|
||||
return; // we're about to leave vblank, stop
|
||||
|
||||
switch (sDma3Requests[sDma3RequestCursor].mode)
|
||||
{
|
||||
case DMA_REQUEST_COPY32: // regular 32-bit copy
|
||||
Dma3CopyLarge32_(sDma3Requests[sDma3RequestCursor].src,
|
||||
sDma3Requests[sDma3RequestCursor].dest,
|
||||
sDma3Requests[sDma3RequestCursor].size);
|
||||
break;
|
||||
case DMA_REQUEST_FILL32: // repeat a single 32-bit value across RAM
|
||||
Dma3FillLarge32_(sDma3Requests[sDma3RequestCursor].value,
|
||||
sDma3Requests[sDma3RequestCursor].dest,
|
||||
sDma3Requests[sDma3RequestCursor].size);
|
||||
break;
|
||||
case DMA_REQUEST_COPY16: // regular 16-bit copy
|
||||
Dma3CopyLarge16_(sDma3Requests[sDma3RequestCursor].src,
|
||||
sDma3Requests[sDma3RequestCursor].dest,
|
||||
sDma3Requests[sDma3RequestCursor].size);
|
||||
break;
|
||||
case DMA_REQUEST_FILL16: // repeat a single 16-bit value across RAM
|
||||
Dma3FillLarge16_(sDma3Requests[sDma3RequestCursor].value,
|
||||
sDma3Requests[sDma3RequestCursor].dest,
|
||||
sDma3Requests[sDma3RequestCursor].size);
|
||||
break;
|
||||
}
|
||||
|
||||
// Free the request
|
||||
sDma3Requests[sDma3RequestCursor].src = NULL;
|
||||
sDma3Requests[sDma3RequestCursor].dest = NULL;
|
||||
sDma3Requests[sDma3RequestCursor].size = 0;
|
||||
sDma3Requests[sDma3RequestCursor].mode = 0;
|
||||
sDma3Requests[sDma3RequestCursor].value = 0;
|
||||
sDma3RequestCursor++;
|
||||
|
||||
if (sDma3RequestCursor >= MAX_DMA_REQUESTS) // loop back to the first DMA request
|
||||
sDma3RequestCursor = 0;
|
||||
}
|
||||
}
|
||||
|
||||
s16 RequestDma3Copy(const void *src, void *dest, u16 size, u8 mode)
|
||||
{
|
||||
int cursor;
|
||||
int i = 0;
|
||||
|
||||
sDma3ManagerLocked = TRUE;
|
||||
cursor = sDma3RequestCursor;
|
||||
|
||||
while (i < MAX_DMA_REQUESTS)
|
||||
{
|
||||
if (sDma3Requests[cursor].size == 0) // an empty request was found.
|
||||
{
|
||||
sDma3Requests[cursor].src = src;
|
||||
sDma3Requests[cursor].dest = dest;
|
||||
sDma3Requests[cursor].size = size;
|
||||
|
||||
if (mode == 1)
|
||||
sDma3Requests[cursor].mode = DMA_REQUEST_COPY32;
|
||||
else
|
||||
sDma3Requests[cursor].mode = DMA_REQUEST_COPY16;
|
||||
|
||||
sDma3ManagerLocked = FALSE;
|
||||
return cursor;
|
||||
}
|
||||
if (++cursor >= MAX_DMA_REQUESTS) // loop back to start.
|
||||
cursor = 0;
|
||||
i++;
|
||||
}
|
||||
sDma3ManagerLocked = FALSE;
|
||||
return -1; // no free DMA request was found
|
||||
}
|
||||
|
||||
s16 RequestDma3Fill(s32 value, void *dest, u16 size, u8 mode)
|
||||
{
|
||||
int cursor;
|
||||
int i = 0;
|
||||
|
||||
cursor = sDma3RequestCursor;
|
||||
sDma3ManagerLocked = TRUE;
|
||||
|
||||
while (i < MAX_DMA_REQUESTS)
|
||||
{
|
||||
if (sDma3Requests[cursor].size == 0) // an empty request was found.
|
||||
{
|
||||
sDma3Requests[cursor].dest = dest;
|
||||
sDma3Requests[cursor].size = size;
|
||||
sDma3Requests[cursor].mode = mode;
|
||||
sDma3Requests[cursor].value = value;
|
||||
|
||||
if(mode == 1)
|
||||
sDma3Requests[cursor].mode = DMA_REQUEST_FILL32;
|
||||
else
|
||||
sDma3Requests[cursor].mode = DMA_REQUEST_FILL16;
|
||||
|
||||
sDma3ManagerLocked = FALSE;
|
||||
return cursor;
|
||||
}
|
||||
if (++cursor >= MAX_DMA_REQUESTS) // loop back to start.
|
||||
cursor = 0;
|
||||
i++;
|
||||
}
|
||||
sDma3ManagerLocked = FALSE;
|
||||
return -1; // no free DMA request was found
|
||||
}
|
||||
|
||||
s16 CheckForSpaceForDma3Request(s16 index)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
if (index == -1) // check if all requests are free
|
||||
{
|
||||
while (i < MAX_DMA_REQUESTS)
|
||||
{
|
||||
if (sDma3Requests[i].size != 0)
|
||||
return -1;
|
||||
i++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else // check the specified request
|
||||
{
|
||||
if (sDma3Requests[index].size != 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
}
|
195
gflib/gpu_regs.c
Normal file
195
gflib/gpu_regs.c
Normal file
@ -0,0 +1,195 @@
|
||||
#include "global.h"
|
||||
#include "gpu_regs.h"
|
||||
|
||||
#define GPU_REG_BUF_SIZE 0x60
|
||||
|
||||
#define GPU_REG_BUF(offset) (*(u16 *)(&sGpuRegBuffer[offset]))
|
||||
#define GPU_REG(offset) (*(vu16 *)(REG_BASE + offset))
|
||||
|
||||
#define EMPTY_SLOT 0xFF
|
||||
|
||||
static u8 sGpuRegBuffer[GPU_REG_BUF_SIZE];
|
||||
static u8 sGpuRegWaitingList[GPU_REG_BUF_SIZE];
|
||||
static volatile bool8 sGpuRegBufferLocked;
|
||||
static volatile bool8 sShouldSyncRegIE;
|
||||
static vu16 sRegIE;
|
||||
|
||||
static void CopyBufferedValueToGpuReg(u8 regOffset);
|
||||
static void SyncRegIE(void);
|
||||
static void UpdateRegDispstatIntrBits(u16 regIE);
|
||||
|
||||
void InitGpuRegManager(void)
|
||||
{
|
||||
s32 i;
|
||||
|
||||
for (i = 0; i < GPU_REG_BUF_SIZE; i++)
|
||||
{
|
||||
sGpuRegBuffer[i] = 0;
|
||||
sGpuRegWaitingList[i] = EMPTY_SLOT;
|
||||
}
|
||||
|
||||
sGpuRegBufferLocked = FALSE;
|
||||
sShouldSyncRegIE = FALSE;
|
||||
sRegIE = 0;
|
||||
}
|
||||
|
||||
static void CopyBufferedValueToGpuReg(u8 regOffset)
|
||||
{
|
||||
if (regOffset == REG_OFFSET_DISPSTAT)
|
||||
{
|
||||
REG_DISPSTAT &= ~(DISPSTAT_HBLANK_INTR | DISPSTAT_VBLANK_INTR);
|
||||
REG_DISPSTAT |= GPU_REG_BUF(REG_OFFSET_DISPSTAT);
|
||||
}
|
||||
else
|
||||
{
|
||||
GPU_REG(regOffset) = GPU_REG_BUF(regOffset);
|
||||
}
|
||||
}
|
||||
|
||||
void CopyBufferedValuesToGpuRegs(void)
|
||||
{
|
||||
if (!sGpuRegBufferLocked)
|
||||
{
|
||||
s32 i;
|
||||
|
||||
for (i = 0; i < GPU_REG_BUF_SIZE; i++)
|
||||
{
|
||||
u8 regOffset = sGpuRegWaitingList[i];
|
||||
if (regOffset == EMPTY_SLOT)
|
||||
return;
|
||||
CopyBufferedValueToGpuReg(regOffset);
|
||||
sGpuRegWaitingList[i] = EMPTY_SLOT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetGpuReg(u8 regOffset, u16 value)
|
||||
{
|
||||
if (regOffset < GPU_REG_BUF_SIZE)
|
||||
{
|
||||
u16 vcount;
|
||||
|
||||
GPU_REG_BUF(regOffset) = value;
|
||||
vcount = REG_VCOUNT & 0xFF;
|
||||
|
||||
if ((vcount >= 161 && vcount <= 225) || (REG_DISPCNT & DISPCNT_FORCED_BLANK))
|
||||
{
|
||||
CopyBufferedValueToGpuReg(regOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
s32 i;
|
||||
|
||||
sGpuRegBufferLocked = TRUE;
|
||||
|
||||
for (i = 0; i < GPU_REG_BUF_SIZE && sGpuRegWaitingList[i] != EMPTY_SLOT; i++)
|
||||
{
|
||||
if (sGpuRegWaitingList[i] == regOffset)
|
||||
{
|
||||
sGpuRegBufferLocked = FALSE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
sGpuRegWaitingList[i] = regOffset;
|
||||
sGpuRegBufferLocked = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetGpuReg_ForcedBlank(u8 regOffset, u16 value)
|
||||
{
|
||||
if (regOffset < GPU_REG_BUF_SIZE)
|
||||
{
|
||||
GPU_REG_BUF(regOffset) = value;
|
||||
|
||||
if (REG_DISPCNT & DISPCNT_FORCED_BLANK)
|
||||
{
|
||||
CopyBufferedValueToGpuReg(regOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
s32 i;
|
||||
|
||||
sGpuRegBufferLocked = TRUE;
|
||||
|
||||
for (i = 0; i < GPU_REG_BUF_SIZE && sGpuRegWaitingList[i] != EMPTY_SLOT; i++)
|
||||
{
|
||||
if (sGpuRegWaitingList[i] == regOffset)
|
||||
{
|
||||
sGpuRegBufferLocked = FALSE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
sGpuRegWaitingList[i] = regOffset;
|
||||
sGpuRegBufferLocked = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u16 GetGpuReg(u8 regOffset)
|
||||
{
|
||||
if (regOffset == REG_OFFSET_DISPSTAT)
|
||||
return REG_DISPSTAT;
|
||||
|
||||
if (regOffset == REG_OFFSET_VCOUNT)
|
||||
return REG_VCOUNT;
|
||||
|
||||
return GPU_REG_BUF(regOffset);
|
||||
}
|
||||
|
||||
void SetGpuRegBits(u8 regOffset, u16 mask)
|
||||
{
|
||||
u16 regValue = GPU_REG_BUF(regOffset);
|
||||
SetGpuReg(regOffset, regValue | mask);
|
||||
}
|
||||
|
||||
void ClearGpuRegBits(u8 regOffset, u16 mask)
|
||||
{
|
||||
u16 regValue = GPU_REG_BUF(regOffset);
|
||||
SetGpuReg(regOffset, regValue & ~mask);
|
||||
}
|
||||
|
||||
static void SyncRegIE(void)
|
||||
{
|
||||
if (sShouldSyncRegIE)
|
||||
{
|
||||
u16 temp = REG_IME;
|
||||
REG_IME = 0;
|
||||
REG_IE = sRegIE;
|
||||
REG_IME = temp;
|
||||
sShouldSyncRegIE = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
void EnableInterrupts(u16 mask)
|
||||
{
|
||||
sRegIE |= mask;
|
||||
sShouldSyncRegIE = TRUE;
|
||||
SyncRegIE();
|
||||
UpdateRegDispstatIntrBits(sRegIE);
|
||||
}
|
||||
|
||||
void DisableInterrupts(u16 mask)
|
||||
{
|
||||
sRegIE &= ~mask;
|
||||
sShouldSyncRegIE = TRUE;
|
||||
SyncRegIE();
|
||||
UpdateRegDispstatIntrBits(sRegIE);
|
||||
}
|
||||
|
||||
static void UpdateRegDispstatIntrBits(u16 regIE)
|
||||
{
|
||||
u16 oldValue = GetGpuReg(REG_OFFSET_DISPSTAT) & (DISPSTAT_HBLANK_INTR | DISPSTAT_VBLANK_INTR);
|
||||
u16 newValue = 0;
|
||||
|
||||
if (regIE & INTR_FLAG_VBLANK)
|
||||
newValue |= DISPSTAT_VBLANK_INTR;
|
||||
|
||||
if (regIE & INTR_FLAG_HBLANK)
|
||||
newValue |= DISPSTAT_HBLANK_INTR;
|
||||
|
||||
if (oldValue != newValue)
|
||||
SetGpuReg(REG_OFFSET_DISPSTAT, newValue);
|
||||
}
|
19
gflib/gpu_regs.h
Normal file
19
gflib/gpu_regs.h
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef GUARD_GPU_REGS_H
|
||||
#define GUARD_GPU_REGS_H
|
||||
|
||||
// Exported type declarations
|
||||
|
||||
// Exported RAM declarations
|
||||
|
||||
// Exported ROM declarations
|
||||
void InitGpuRegManager(void);
|
||||
void CopyBufferedValuesToGpuRegs(void);
|
||||
void SetGpuReg(u8 regOffset, u16 value);
|
||||
void SetGpuReg_ForcedBlank(u8 regOffset, u16 value);
|
||||
u16 GetGpuReg(u8 regOffset);
|
||||
void SetGpuRegBits(u8 regOffset, u16 mask);
|
||||
void ClearGpuRegBits(u8 regOffset, u16 mask);
|
||||
void EnableInterrupts(u16 mask);
|
||||
void DisableInterrupts(u16 mask);
|
||||
|
||||
#endif //GUARD_GPU_REGS_H
|
36
gflib/io_reg.c
Normal file
36
gflib/io_reg.c
Normal file
@ -0,0 +1,36 @@
|
||||
#include "global.h"
|
||||
#include "io_reg.h"
|
||||
#include "gba/io_reg.h"
|
||||
|
||||
static const u32 sUnused[] = {
|
||||
0,
|
||||
0,
|
||||
(1 << 26) | (1 << 3),
|
||||
(1 << 26) | (1 << 3) | (1 << 1),
|
||||
(1 << 26) | (1 << 3) | (1 << 2),
|
||||
(1 << 26) | (1 << 3) | (1 << 2) | (1 << 1),
|
||||
(1 << 26) | (1 << 4),
|
||||
(1 << 26) | (1 << 4) | (1 << 2),
|
||||
(1 << 26) | (1 << 4) | (1 << 3),
|
||||
(1 << 26) | (1 << 4) | (1 << 3) | (1 << 2),
|
||||
(1 << 26) | (1 << 4) | (1 << 1),
|
||||
(1 << 26) | (1 << 4) | (1 << 2) | (1 << 1),
|
||||
(1 << 26) | (1 << 4) | (1 << 3) | (1 << 1),
|
||||
(1 << 26) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1),
|
||||
(1 << 25) | (1 << 8),
|
||||
(1 << 27) | (1 << 10),
|
||||
};
|
||||
|
||||
const u16 gOverworldBackgroundLayerFlags[] = {
|
||||
BLDCNT_TGT2_BG0,
|
||||
BLDCNT_TGT2_BG1,
|
||||
BLDCNT_TGT2_BG2,
|
||||
BLDCNT_TGT2_BG3,
|
||||
};
|
||||
|
||||
const u16 gOrbEffectBackgroundLayerFlags[] = {
|
||||
BLDCNT_TGT1_BG0,
|
||||
BLDCNT_TGT1_BG1,
|
||||
BLDCNT_TGT1_BG2,
|
||||
BLDCNT_TGT1_BG3,
|
||||
};
|
7
gflib/io_reg.h
Normal file
7
gflib/io_reg.h
Normal file
@ -0,0 +1,7 @@
|
||||
#ifndef GUARD_IO_REG_H
|
||||
#define GUARD_IO_REG_H
|
||||
|
||||
extern const u16 gOverworldBackgroundLayerFlags[];
|
||||
extern const u16 gOrbEffectBackgroundLayerFlags[];
|
||||
|
||||
#endif // GUARD_IO_REG_H
|
209
gflib/malloc.c
Normal file
209
gflib/malloc.c
Normal file
@ -0,0 +1,209 @@
|
||||
#include "global.h"
|
||||
|
||||
static void *sHeapStart;
|
||||
static u32 sHeapSize;
|
||||
|
||||
#define MALLOC_SYSTEM_ID 0xA3A3
|
||||
|
||||
struct MemBlock {
|
||||
// Whether this block is currently allocated.
|
||||
bool16 flag;
|
||||
|
||||
// Magic number used for error checking. Should equal MALLOC_SYSTEM_ID.
|
||||
u16 magic;
|
||||
|
||||
// Size of the block (not including this header struct).
|
||||
u32 size;
|
||||
|
||||
// Previous block pointer. Equals sHeapStart if this is the first block.
|
||||
struct MemBlock *prev;
|
||||
|
||||
// Next block pointer. Equals sHeapStart if this is the last block.
|
||||
struct MemBlock *next;
|
||||
|
||||
// Data in the memory block. (Arrays of length 0 are a GNU extension.)
|
||||
u8 data[0];
|
||||
};
|
||||
|
||||
void PutMemBlockHeader(void *block, struct MemBlock *prev, struct MemBlock *next, u32 size)
|
||||
{
|
||||
struct MemBlock *header = (struct MemBlock *)block;
|
||||
|
||||
header->flag = FALSE;
|
||||
header->magic = MALLOC_SYSTEM_ID;
|
||||
header->size = size;
|
||||
header->prev = prev;
|
||||
header->next = next;
|
||||
}
|
||||
|
||||
void PutFirstMemBlockHeader(void *block, u32 size)
|
||||
{
|
||||
PutMemBlockHeader(block, (struct MemBlock *)block, (struct MemBlock *)block, size - sizeof(struct MemBlock));
|
||||
}
|
||||
|
||||
void *AllocInternal(void *heapStart, u32 size)
|
||||
{
|
||||
struct MemBlock *pos = (struct MemBlock *)heapStart;
|
||||
struct MemBlock *head = pos;
|
||||
struct MemBlock *splitBlock;
|
||||
u32 foundBlockSize;
|
||||
|
||||
// Alignment
|
||||
if (size & 3)
|
||||
size = 4 * ((size / 4) + 1);
|
||||
|
||||
for (;;) {
|
||||
// Loop through the blocks looking for unused block that's big enough.
|
||||
|
||||
if (!pos->flag) {
|
||||
foundBlockSize = pos->size;
|
||||
|
||||
if (foundBlockSize >= size) {
|
||||
if (foundBlockSize - size < 2 * sizeof(struct MemBlock)) {
|
||||
// The block isn't much bigger than the requested size,
|
||||
// so just use it.
|
||||
pos->flag = TRUE;
|
||||
} else {
|
||||
// The block is significantly bigger than the requested
|
||||
// size, so split the rest into a separate block.
|
||||
foundBlockSize -= sizeof(struct MemBlock);
|
||||
foundBlockSize -= size;
|
||||
|
||||
splitBlock = (struct MemBlock *)(pos->data + size);
|
||||
|
||||
pos->flag = TRUE;
|
||||
pos->size = size;
|
||||
|
||||
PutMemBlockHeader(splitBlock, pos, pos->next, foundBlockSize);
|
||||
|
||||
pos->next = splitBlock;
|
||||
|
||||
if (splitBlock->next != head)
|
||||
splitBlock->next->prev = splitBlock;
|
||||
}
|
||||
|
||||
return pos->data;
|
||||
}
|
||||
}
|
||||
|
||||
if (pos->next == head)
|
||||
return NULL;
|
||||
|
||||
pos = pos->next;
|
||||
}
|
||||
}
|
||||
|
||||
void FreeInternal(void *heapStart, void *pointer)
|
||||
{
|
||||
if (pointer) {
|
||||
struct MemBlock *head = (struct MemBlock *)heapStart;
|
||||
struct MemBlock *block = (struct MemBlock *)((u8 *)pointer - sizeof(struct MemBlock));
|
||||
block->flag = FALSE;
|
||||
|
||||
// If the freed block isn't the last one, merge with the next block
|
||||
// if it's not in use.
|
||||
if (block->next != head) {
|
||||
if (!block->next->flag) {
|
||||
block->size += sizeof(struct MemBlock) + block->next->size;
|
||||
block->next->magic = 0;
|
||||
block->next = block->next->next;
|
||||
if (block->next != head)
|
||||
block->next->prev = block;
|
||||
}
|
||||
}
|
||||
|
||||
// If the freed block isn't the first one, merge with the previous block
|
||||
// if it's not in use.
|
||||
if (block != head) {
|
||||
if (!block->prev->flag) {
|
||||
block->prev->next = block->next;
|
||||
|
||||
if (block->next != head)
|
||||
block->next->prev = block->prev;
|
||||
|
||||
block->magic = 0;
|
||||
block->prev->size += sizeof(struct MemBlock) + block->size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void *AllocZeroedInternal(void *heapStart, u32 size)
|
||||
{
|
||||
void *mem = AllocInternal(heapStart, size);
|
||||
|
||||
if (mem != NULL) {
|
||||
if (size & 3)
|
||||
size = 4 * ((size / 4) + 1);
|
||||
|
||||
CpuFill32(0, mem, size);
|
||||
}
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
||||
bool32 CheckMemBlockInternal(void *heapStart, void *pointer)
|
||||
{
|
||||
struct MemBlock *head = (struct MemBlock *)heapStart;
|
||||
struct MemBlock *block = (struct MemBlock *)((u8 *)pointer - sizeof(struct MemBlock));
|
||||
|
||||
if (block->magic != MALLOC_SYSTEM_ID)
|
||||
return FALSE;
|
||||
|
||||
if (block->next->magic != MALLOC_SYSTEM_ID)
|
||||
return FALSE;
|
||||
|
||||
if (block->next != head && block->next->prev != block)
|
||||
return FALSE;
|
||||
|
||||
if (block->prev->magic != MALLOC_SYSTEM_ID)
|
||||
return FALSE;
|
||||
|
||||
if (block->prev != head && block->prev->next != block)
|
||||
return FALSE;
|
||||
|
||||
if (block->next != head && block->next != (struct MemBlock *)(block->data + block->size))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void InitHeap(void *heapStart, u32 heapSize)
|
||||
{
|
||||
sHeapStart = heapStart;
|
||||
sHeapSize = heapSize;
|
||||
PutFirstMemBlockHeader(heapStart, heapSize);
|
||||
}
|
||||
|
||||
void *Alloc(u32 size)
|
||||
{
|
||||
return AllocInternal(sHeapStart, size);
|
||||
}
|
||||
|
||||
void *AllocZeroed(u32 size)
|
||||
{
|
||||
return AllocZeroedInternal(sHeapStart, size);
|
||||
}
|
||||
|
||||
void Free(void *pointer)
|
||||
{
|
||||
FreeInternal(sHeapStart, pointer);
|
||||
}
|
||||
|
||||
bool32 CheckMemBlock(void *pointer)
|
||||
{
|
||||
return CheckMemBlockInternal(sHeapStart, pointer);
|
||||
}
|
||||
|
||||
bool32 CheckHeap()
|
||||
{
|
||||
struct MemBlock *pos = (struct MemBlock *)sHeapStart;
|
||||
|
||||
do {
|
||||
if (!CheckMemBlockInternal(sHeapStart, pos->data))
|
||||
return FALSE;
|
||||
pos = pos->next;
|
||||
} while (pos != (struct MemBlock *)sHeapStart);
|
||||
|
||||
return TRUE;
|
||||
}
|
24
gflib/malloc.h
Normal file
24
gflib/malloc.h
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef GUARD_ALLOC_H
|
||||
#define GUARD_ALLOC_H
|
||||
|
||||
#define HEAP_SIZE 0x1C000
|
||||
#define malloc Alloc
|
||||
#define calloc(ct, sz) AllocZeroed((ct) * (sz))
|
||||
#define free Free
|
||||
|
||||
#define FREE_AND_SET_NULL(ptr) \
|
||||
{ \
|
||||
free(ptr); \
|
||||
ptr = NULL; \
|
||||
}
|
||||
|
||||
#define TRY_FREE_AND_SET_NULL(ptr) if (ptr != NULL) FREE_AND_SET_NULL(ptr)
|
||||
|
||||
extern u8 gHeap[];
|
||||
|
||||
void *Alloc(u32 size);
|
||||
void *AllocZeroed(u32 size);
|
||||
void Free(void *pointer);
|
||||
void InitHeap(void *pointer, u32 size);
|
||||
|
||||
#endif // GUARD_ALLOC_H
|
1760
gflib/sprite.c
Normal file
1760
gflib/sprite.c
Normal file
@ -0,0 +1,1760 @@
|
||||
#include "global.h"
|
||||
#include "sprite.h"
|
||||
#include "main.h"
|
||||
#include "palette.h"
|
||||
|
||||
#define MAX_SPRITE_COPY_REQUESTS 64
|
||||
|
||||
#define OAM_MATRIX_COUNT 32
|
||||
|
||||
#define sAnchorX data[6]
|
||||
#define sAnchorY data[7]
|
||||
|
||||
#define SET_SPRITE_TILE_RANGE(index, start, count) \
|
||||
{ \
|
||||
sSpriteTileRanges[index * 2] = start; \
|
||||
(sSpriteTileRanges + 1)[index * 2] = count; \
|
||||
}
|
||||
|
||||
#define ALLOC_SPRITE_TILE(n) \
|
||||
{ \
|
||||
sSpriteTileAllocBitmap[(n) / 8] |= (1 << ((n) % 8)); \
|
||||
}
|
||||
|
||||
#define FREE_SPRITE_TILE(n) \
|
||||
{ \
|
||||
sSpriteTileAllocBitmap[(n) / 8] &= ~(1 << ((n) % 8)); \
|
||||
}
|
||||
|
||||
#define SPRITE_TILE_IS_ALLOCATED(n) ((sSpriteTileAllocBitmap[(n) / 8] >> ((n) % 8)) & 1)
|
||||
|
||||
|
||||
struct SpriteCopyRequest
|
||||
{
|
||||
const u8 *src;
|
||||
u8 *dest;
|
||||
u16 size;
|
||||
};
|
||||
|
||||
struct OamDimensions32
|
||||
{
|
||||
s32 width;
|
||||
s32 height;
|
||||
};
|
||||
|
||||
struct OamDimensions
|
||||
{
|
||||
s8 width;
|
||||
s8 height;
|
||||
};
|
||||
|
||||
static void UpdateOamCoords(void);
|
||||
static void BuildSpritePriorities(void);
|
||||
static void SortSprites(void);
|
||||
static void CopyMatricesToOamBuffer(void);
|
||||
static void AddSpritesToOamBuffer(void);
|
||||
static u8 CreateSpriteAt(u8 index, const struct SpriteTemplate *template, s16 x, s16 y, u8 subpriority);
|
||||
static void ResetOamMatrices(void);
|
||||
static void ResetSprite(struct Sprite *sprite);
|
||||
static s16 AllocSpriteTiles(u16 tileCount);
|
||||
static void RequestSpriteFrameImageCopy(u16 index, u16 tileNum, const struct SpriteFrameImage *images);
|
||||
static void ResetAllSprites(void);
|
||||
static void BeginAnim(struct Sprite *sprite);
|
||||
static void ContinueAnim(struct Sprite *sprite);
|
||||
static void AnimCmd_frame(struct Sprite *sprite);
|
||||
static void AnimCmd_end(struct Sprite *sprite);
|
||||
static void AnimCmd_jump(struct Sprite *sprite);
|
||||
static void AnimCmd_loop(struct Sprite *sprite);
|
||||
static void BeginAnimLoop(struct Sprite *sprite);
|
||||
static void ContinueAnimLoop(struct Sprite *sprite);
|
||||
static void JumpToTopOfAnimLoop(struct Sprite *sprite);
|
||||
static void BeginAffineAnim(struct Sprite *sprite);
|
||||
static void ContinueAffineAnim(struct Sprite *sprite);
|
||||
static void AffineAnimDelay(u8 matrixNum, struct Sprite *sprite);
|
||||
static void AffineAnimCmd_loop(u8 matrixNum, struct Sprite *sprite);
|
||||
static void BeginAffineAnimLoop(u8 matrixNum, struct Sprite *sprite);
|
||||
static void ContinueAffineAnimLoop(u8 matrixNum, struct Sprite *sprite);
|
||||
static void JumpToTopOfAffineAnimLoop(u8 matrixNum, struct Sprite *sprite);
|
||||
static void AffineAnimCmd_jump(u8 matrixNum, struct Sprite *sprite);
|
||||
static void AffineAnimCmd_end(u8 matrixNum, struct Sprite *sprite);
|
||||
static void AffineAnimCmd_frame(u8 matrixNum, struct Sprite *sprite);
|
||||
static void CopyOamMatrix(u8 destMatrixIndex, struct OamMatrix *srcMatrix);
|
||||
static u8 GetSpriteMatrixNum(struct Sprite *sprite);
|
||||
static void SetSpriteOamFlipBits(struct Sprite *sprite, u8 hFlip, u8 vFlip);
|
||||
static void AffineAnimStateRestartAnim(u8 matrixNum);
|
||||
static void AffineAnimStateStartAnim(u8 matrixNum, u8 animNum);
|
||||
static void AffineAnimStateReset(u8 matrixNum);
|
||||
static void ApplyAffineAnimFrameAbsolute(u8 matrixNum, struct AffineAnimFrameCmd *frameCmd);
|
||||
static void DecrementAnimDelayCounter(struct Sprite *sprite);
|
||||
static bool8 DecrementAffineAnimDelayCounter(struct Sprite *sprite, u8 matrixNum);
|
||||
static void ApplyAffineAnimFrameRelativeAndUpdateMatrix(u8 matrixNum, struct AffineAnimFrameCmd *frameCmd);
|
||||
static s16 ConvertScaleParam(s16 scale);
|
||||
static void GetAffineAnimFrame(u8 matrixNum, struct Sprite *sprite, struct AffineAnimFrameCmd *frameCmd);
|
||||
static void ApplyAffineAnimFrame(u8 matrixNum, struct AffineAnimFrameCmd *frameCmd);
|
||||
static u8 IndexOfSpriteTileTag(u16 tag);
|
||||
static void AllocSpriteTileRange(u16 tag, u16 start, u16 count);
|
||||
static void DoLoadSpritePalette(const u16 *src, u16 paletteOffset);
|
||||
static void UpdateSpriteMatrixAnchorPos(struct Sprite* sprite, s32 a1, s32 a2);
|
||||
|
||||
typedef void (*AnimFunc)(struct Sprite *);
|
||||
typedef void (*AnimCmdFunc)(struct Sprite *);
|
||||
typedef void (*AffineAnimCmdFunc)(u8 matrixNum, struct Sprite *);
|
||||
|
||||
#define DUMMY_OAM_DATA \
|
||||
{ \
|
||||
.y = DISPLAY_HEIGHT, \
|
||||
.affineMode = 0, \
|
||||
.objMode = 0, \
|
||||
.mosaic = 0, \
|
||||
.bpp = 0, \
|
||||
.shape = SPRITE_SHAPE(8x8), \
|
||||
.x = DISPLAY_WIDTH + 64, \
|
||||
.matrixNum = 0, \
|
||||
.size = SPRITE_SIZE(8x8), \
|
||||
.tileNum = 0, \
|
||||
.priority = 3, /* lowest priority */ \
|
||||
.paletteNum = 0, \
|
||||
.affineParam = 0 \
|
||||
}
|
||||
|
||||
#define ANIM_END 0xFFFF
|
||||
#define AFFINE_ANIM_END 0x7FFF
|
||||
|
||||
// forward declarations
|
||||
const union AnimCmd * const gDummySpriteAnimTable[];
|
||||
const union AffineAnimCmd * const gDummySpriteAffineAnimTable[];
|
||||
const struct SpriteTemplate gDummySpriteTemplate;
|
||||
|
||||
// Unreferenced data. Also unreferenced in R/S.
|
||||
static const u8 sUnknownData[24] =
|
||||
{
|
||||
0x01, 0x04, 0x10, 0x40,
|
||||
0x02, 0x04, 0x08, 0x20,
|
||||
0x02, 0x04, 0x08, 0x20,
|
||||
0x01, 0x04, 0x10, 0x40,
|
||||
0x02, 0x04, 0x08, 0x20,
|
||||
0x02, 0x04, 0x08, 0x20,
|
||||
};
|
||||
|
||||
static const u8 sCenterToCornerVecTable[3][4][2] =
|
||||
{
|
||||
{ // square
|
||||
{ -4, -4 },
|
||||
{ -8, -8 },
|
||||
{ -16, -16 },
|
||||
{ -32, -32 },
|
||||
},
|
||||
{ // horizontal rectangle
|
||||
{ -8, -4 },
|
||||
{ -16, -4 },
|
||||
{ -16, -8 },
|
||||
{ -32, -16 },
|
||||
},
|
||||
{ // vertical rectangle
|
||||
{ -4, -8 },
|
||||
{ -4, -16 },
|
||||
{ -8, -16 },
|
||||
{ -16, -32 },
|
||||
},
|
||||
};
|
||||
|
||||
static const struct Sprite sDummySprite =
|
||||
{
|
||||
.oam = DUMMY_OAM_DATA,
|
||||
.anims = gDummySpriteAnimTable,
|
||||
.affineAnims = gDummySpriteAffineAnimTable,
|
||||
.template = &gDummySpriteTemplate,
|
||||
.callback = SpriteCallbackDummy,
|
||||
.x = DISPLAY_WIDTH + 64,
|
||||
.y = DISPLAY_HEIGHT,
|
||||
.subpriority = 0xFF
|
||||
};
|
||||
|
||||
const struct OamData gDummyOamData = DUMMY_OAM_DATA;
|
||||
|
||||
static const union AnimCmd sDummyAnim = { ANIM_END };
|
||||
|
||||
const union AnimCmd * const gDummySpriteAnimTable[] = { &sDummyAnim };
|
||||
|
||||
static const union AffineAnimCmd sDummyAffineAnim = { AFFINE_ANIM_END };
|
||||
|
||||
const union AffineAnimCmd * const gDummySpriteAffineAnimTable[] = { &sDummyAffineAnim };
|
||||
|
||||
const struct SpriteTemplate gDummySpriteTemplate =
|
||||
{
|
||||
.tileTag = 0,
|
||||
.paletteTag = TAG_NONE,
|
||||
.oam = &gDummyOamData,
|
||||
.anims = gDummySpriteAnimTable,
|
||||
.images = NULL,
|
||||
.affineAnims = gDummySpriteAffineAnimTable,
|
||||
.callback = SpriteCallbackDummy
|
||||
};
|
||||
|
||||
static const AnimFunc sAnimFuncs[] =
|
||||
{
|
||||
ContinueAnim,
|
||||
BeginAnim,
|
||||
};
|
||||
|
||||
static const AnimFunc sAffineAnimFuncs[] =
|
||||
{
|
||||
ContinueAffineAnim,
|
||||
BeginAffineAnim,
|
||||
};
|
||||
|
||||
static const AnimCmdFunc sAnimCmdFuncs[] =
|
||||
{
|
||||
AnimCmd_loop,
|
||||
AnimCmd_jump,
|
||||
AnimCmd_end,
|
||||
AnimCmd_frame,
|
||||
};
|
||||
|
||||
static const AffineAnimCmdFunc sAffineAnimCmdFuncs[] =
|
||||
{
|
||||
AffineAnimCmd_loop,
|
||||
AffineAnimCmd_jump,
|
||||
AffineAnimCmd_end,
|
||||
AffineAnimCmd_frame,
|
||||
};
|
||||
|
||||
static const struct OamDimensions32 sOamDimensions32[3][4] =
|
||||
{
|
||||
[ST_OAM_SQUARE] =
|
||||
{
|
||||
[SPRITE_SIZE(8x8)] = { 8, 8 },
|
||||
[SPRITE_SIZE(16x16)] = { 16, 16 },
|
||||
[SPRITE_SIZE(32x32)] = { 32, 32 },
|
||||
[SPRITE_SIZE(64x64)] = { 64, 64 },
|
||||
},
|
||||
[ST_OAM_H_RECTANGLE] =
|
||||
{
|
||||
[SPRITE_SIZE(16x8)] = { 16, 8 },
|
||||
[SPRITE_SIZE(32x8)] = { 32, 8 },
|
||||
[SPRITE_SIZE(32x16)] = { 32, 16 },
|
||||
[SPRITE_SIZE(64x32)] = { 64, 32 },
|
||||
},
|
||||
[ST_OAM_V_RECTANGLE] =
|
||||
{
|
||||
[SPRITE_SIZE(8x16)] = { 8, 16 },
|
||||
[SPRITE_SIZE(8x32)] = { 8, 32 },
|
||||
[SPRITE_SIZE(16x32)] = { 16, 32 },
|
||||
[SPRITE_SIZE(32x64)] = { 32, 64 },
|
||||
},
|
||||
};
|
||||
|
||||
static const struct OamDimensions sOamDimensions[3][4] =
|
||||
{
|
||||
[ST_OAM_SQUARE] =
|
||||
{
|
||||
[SPRITE_SIZE(8x8)] = { 8, 8 },
|
||||
[SPRITE_SIZE(16x16)] = { 16, 16 },
|
||||
[SPRITE_SIZE(32x32)] = { 32, 32 },
|
||||
[SPRITE_SIZE(64x64)] = { 64, 64 },
|
||||
},
|
||||
[ST_OAM_H_RECTANGLE] =
|
||||
{
|
||||
[SPRITE_SIZE(16x8)] = { 16, 8 },
|
||||
[SPRITE_SIZE(32x8)] = { 32, 8 },
|
||||
[SPRITE_SIZE(32x16)] = { 32, 16 },
|
||||
[SPRITE_SIZE(64x32)] = { 64, 32 },
|
||||
},
|
||||
[ST_OAM_V_RECTANGLE] =
|
||||
{
|
||||
[SPRITE_SIZE(8x16)] = { 8, 16 },
|
||||
[SPRITE_SIZE(8x32)] = { 8, 32 },
|
||||
[SPRITE_SIZE(16x32)] = { 16, 32 },
|
||||
[SPRITE_SIZE(32x64)] = { 32, 64 },
|
||||
},
|
||||
};
|
||||
|
||||
// iwram bss
|
||||
static u16 sSpriteTileRangeTags[MAX_SPRITES];
|
||||
static u16 sSpriteTileRanges[MAX_SPRITES * 2];
|
||||
static struct AffineAnimState sAffineAnimStates[OAM_MATRIX_COUNT];
|
||||
static u16 sSpritePaletteTags[16];
|
||||
|
||||
// iwram common
|
||||
u32 gOamMatrixAllocBitmap;
|
||||
u8 gReservedSpritePaletteCount;
|
||||
|
||||
EWRAM_DATA struct Sprite gSprites[MAX_SPRITES + 1] = {0};
|
||||
EWRAM_DATA static u16 sSpritePriorities[MAX_SPRITES] = {0};
|
||||
EWRAM_DATA static u8 sSpriteOrder[MAX_SPRITES] = {0};
|
||||
EWRAM_DATA static bool8 sShouldProcessSpriteCopyRequests = 0;
|
||||
EWRAM_DATA static u8 sSpriteCopyRequestCount = 0;
|
||||
EWRAM_DATA static struct SpriteCopyRequest sSpriteCopyRequests[MAX_SPRITES] = {0};
|
||||
EWRAM_DATA u8 gOamLimit = 0;
|
||||
EWRAM_DATA u16 gReservedSpriteTileCount = 0;
|
||||
EWRAM_DATA static u8 sSpriteTileAllocBitmap[128] = {0};
|
||||
EWRAM_DATA s16 gSpriteCoordOffsetX = 0;
|
||||
EWRAM_DATA s16 gSpriteCoordOffsetY = 0;
|
||||
EWRAM_DATA struct OamMatrix gOamMatrices[OAM_MATRIX_COUNT] = {0};
|
||||
EWRAM_DATA bool8 gAffineAnimsDisabled = FALSE;
|
||||
|
||||
void ResetSpriteData(void)
|
||||
{
|
||||
ResetOamRange(0, 128);
|
||||
ResetAllSprites();
|
||||
ClearSpriteCopyRequests();
|
||||
ResetAffineAnimData();
|
||||
FreeSpriteTileRanges();
|
||||
gOamLimit = 64;
|
||||
gReservedSpriteTileCount = 0;
|
||||
AllocSpriteTiles(0);
|
||||
gSpriteCoordOffsetX = 0;
|
||||
gSpriteCoordOffsetY = 0;
|
||||
}
|
||||
|
||||
void AnimateSprites(void)
|
||||
{
|
||||
u8 i;
|
||||
for (i = 0; i < MAX_SPRITES; i++)
|
||||
{
|
||||
struct Sprite *sprite = &gSprites[i];
|
||||
|
||||
if (sprite->inUse)
|
||||
{
|
||||
sprite->callback(sprite);
|
||||
|
||||
if (sprite->inUse)
|
||||
AnimateSprite(sprite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BuildOamBuffer(void)
|
||||
{
|
||||
u8 temp;
|
||||
UpdateOamCoords();
|
||||
BuildSpritePriorities();
|
||||
SortSprites();
|
||||
temp = gMain.oamLoadDisabled;
|
||||
gMain.oamLoadDisabled = TRUE;
|
||||
AddSpritesToOamBuffer();
|
||||
CopyMatricesToOamBuffer();
|
||||
gMain.oamLoadDisabled = temp;
|
||||
sShouldProcessSpriteCopyRequests = TRUE;
|
||||
}
|
||||
|
||||
void UpdateOamCoords(void)
|
||||
{
|
||||
u8 i;
|
||||
for (i = 0; i < MAX_SPRITES; i++)
|
||||
{
|
||||
struct Sprite *sprite = &gSprites[i];
|
||||
if (sprite->inUse && !sprite->invisible)
|
||||
{
|
||||
if (sprite->coordOffsetEnabled)
|
||||
{
|
||||
sprite->oam.x = sprite->x + sprite->x2 + sprite->centerToCornerVecX + gSpriteCoordOffsetX;
|
||||
sprite->oam.y = sprite->y + sprite->y2 + sprite->centerToCornerVecY + gSpriteCoordOffsetY;
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite->oam.x = sprite->x + sprite->x2 + sprite->centerToCornerVecX;
|
||||
sprite->oam.y = sprite->y + sprite->y2 + sprite->centerToCornerVecY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BuildSpritePriorities(void)
|
||||
{
|
||||
u16 i;
|
||||
for (i = 0; i < MAX_SPRITES; i++)
|
||||
{
|
||||
struct Sprite *sprite = &gSprites[i];
|
||||
u16 priority = sprite->subpriority | (sprite->oam.priority << 8);
|
||||
sSpritePriorities[i] = priority;
|
||||
}
|
||||
}
|
||||
|
||||
void SortSprites(void)
|
||||
{
|
||||
u8 i;
|
||||
for (i = 1; i < MAX_SPRITES; i++)
|
||||
{
|
||||
u8 j = i;
|
||||
struct Sprite *sprite1 = &gSprites[sSpriteOrder[i - 1]];
|
||||
struct Sprite *sprite2 = &gSprites[sSpriteOrder[i]];
|
||||
u16 sprite1Priority = sSpritePriorities[sSpriteOrder[i - 1]];
|
||||
u16 sprite2Priority = sSpritePriorities[sSpriteOrder[i]];
|
||||
s16 sprite1Y = sprite1->oam.y;
|
||||
s16 sprite2Y = sprite2->oam.y;
|
||||
|
||||
if (sprite1Y >= DISPLAY_HEIGHT)
|
||||
sprite1Y = sprite1Y - 256;
|
||||
|
||||
if (sprite2Y >= DISPLAY_HEIGHT)
|
||||
sprite2Y = sprite2Y - 256;
|
||||
|
||||
if (sprite1->oam.affineMode == ST_OAM_AFFINE_DOUBLE
|
||||
&& sprite1->oam.size == ST_OAM_SIZE_3)
|
||||
{
|
||||
u32 shape = sprite1->oam.shape;
|
||||
if (shape == ST_OAM_SQUARE || shape == ST_OAM_V_RECTANGLE)
|
||||
{
|
||||
if (sprite1Y > 128)
|
||||
sprite1Y = sprite1Y - 256;
|
||||
}
|
||||
}
|
||||
|
||||
if (sprite2->oam.affineMode == ST_OAM_AFFINE_DOUBLE
|
||||
&& sprite2->oam.size == ST_OAM_SIZE_3)
|
||||
{
|
||||
u32 shape = sprite2->oam.shape;
|
||||
if (shape == ST_OAM_SQUARE || shape == ST_OAM_V_RECTANGLE)
|
||||
{
|
||||
if (sprite2Y > 128)
|
||||
sprite2Y = sprite2Y - 256;
|
||||
}
|
||||
}
|
||||
|
||||
while (j > 0
|
||||
&& ((sprite1Priority > sprite2Priority)
|
||||
|| (sprite1Priority == sprite2Priority && sprite1Y < sprite2Y)))
|
||||
{
|
||||
u8 temp = sSpriteOrder[j];
|
||||
sSpriteOrder[j] = sSpriteOrder[j - 1];
|
||||
sSpriteOrder[j - 1] = temp;
|
||||
|
||||
// UB: If j equals 1, then j-- makes j equal 0.
|
||||
// Then, sSpriteOrder[-1] gets accessed below.
|
||||
// Although this doesn't result in a bug in the ROM,
|
||||
// the behavior is undefined.
|
||||
j--;
|
||||
#ifdef UBFIX
|
||||
if (j == 0)
|
||||
break;
|
||||
#endif
|
||||
|
||||
sprite1 = &gSprites[sSpriteOrder[j - 1]];
|
||||
sprite2 = &gSprites[sSpriteOrder[j]];
|
||||
sprite1Priority = sSpritePriorities[sSpriteOrder[j - 1]];
|
||||
sprite2Priority = sSpritePriorities[sSpriteOrder[j]];
|
||||
sprite1Y = sprite1->oam.y;
|
||||
sprite2Y = sprite2->oam.y;
|
||||
|
||||
if (sprite1Y >= DISPLAY_HEIGHT)
|
||||
sprite1Y = sprite1Y - 256;
|
||||
|
||||
if (sprite2Y >= DISPLAY_HEIGHT)
|
||||
sprite2Y = sprite2Y - 256;
|
||||
|
||||
if (sprite1->oam.affineMode == ST_OAM_AFFINE_DOUBLE
|
||||
&& sprite1->oam.size == ST_OAM_SIZE_3)
|
||||
{
|
||||
u32 shape = sprite1->oam.shape;
|
||||
if (shape == ST_OAM_SQUARE || shape == ST_OAM_V_RECTANGLE)
|
||||
{
|
||||
if (sprite1Y > 128)
|
||||
sprite1Y = sprite1Y - 256;
|
||||
}
|
||||
}
|
||||
|
||||
if (sprite2->oam.affineMode == ST_OAM_AFFINE_DOUBLE
|
||||
&& sprite2->oam.size == ST_OAM_SIZE_3)
|
||||
{
|
||||
u32 shape = sprite2->oam.shape;
|
||||
if (shape == ST_OAM_SQUARE || shape == ST_OAM_V_RECTANGLE)
|
||||
{
|
||||
if (sprite2Y > 128)
|
||||
sprite2Y = sprite2Y - 256;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CopyMatricesToOamBuffer(void)
|
||||
{
|
||||
u8 i;
|
||||
for (i = 0; i < OAM_MATRIX_COUNT; i++)
|
||||
{
|
||||
u32 base = 4 * i;
|
||||
gMain.oamBuffer[base + 0].affineParam = gOamMatrices[i].a;
|
||||
gMain.oamBuffer[base + 1].affineParam = gOamMatrices[i].b;
|
||||
gMain.oamBuffer[base + 2].affineParam = gOamMatrices[i].c;
|
||||
gMain.oamBuffer[base + 3].affineParam = gOamMatrices[i].d;
|
||||
}
|
||||
}
|
||||
|
||||
void AddSpritesToOamBuffer(void)
|
||||
{
|
||||
u8 i = 0;
|
||||
u8 oamIndex = 0;
|
||||
|
||||
while (i < MAX_SPRITES)
|
||||
{
|
||||
struct Sprite *sprite = &gSprites[sSpriteOrder[i]];
|
||||
if (sprite->inUse && !sprite->invisible && AddSpriteToOamBuffer(sprite, &oamIndex))
|
||||
return;
|
||||
i++;
|
||||
}
|
||||
|
||||
while (oamIndex < gOamLimit)
|
||||
{
|
||||
gMain.oamBuffer[oamIndex] = gDummyOamData;
|
||||
oamIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
u8 CreateSprite(const struct SpriteTemplate *template, s16 x, s16 y, u8 subpriority)
|
||||
{
|
||||
u8 i;
|
||||
|
||||
for (i = 0; i < MAX_SPRITES; i++)
|
||||
if (!gSprites[i].inUse)
|
||||
return CreateSpriteAt(i, template, x, y, subpriority);
|
||||
|
||||
return MAX_SPRITES;
|
||||
}
|
||||
|
||||
u8 CreateSpriteAtEnd(const struct SpriteTemplate *template, s16 x, s16 y, u8 subpriority)
|
||||
{
|
||||
s16 i;
|
||||
|
||||
for (i = MAX_SPRITES - 1; i > -1; i--)
|
||||
if (!gSprites[i].inUse)
|
||||
return CreateSpriteAt(i, template, x, y, subpriority);
|
||||
|
||||
return MAX_SPRITES;
|
||||
}
|
||||
|
||||
u8 CreateInvisibleSprite(void (*callback)(struct Sprite *))
|
||||
{
|
||||
u8 index = CreateSprite(&gDummySpriteTemplate, 0, 0, 31);
|
||||
|
||||
if (index == MAX_SPRITES)
|
||||
{
|
||||
return MAX_SPRITES;
|
||||
}
|
||||
else
|
||||
{
|
||||
gSprites[index].invisible = TRUE;
|
||||
gSprites[index].callback = callback;
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
u8 CreateSpriteAt(u8 index, const struct SpriteTemplate *template, s16 x, s16 y, u8 subpriority)
|
||||
{
|
||||
struct Sprite *sprite = &gSprites[index];
|
||||
|
||||
ResetSprite(sprite);
|
||||
|
||||
sprite->inUse = TRUE;
|
||||
sprite->animBeginning = TRUE;
|
||||
sprite->affineAnimBeginning = TRUE;
|
||||
sprite->usingSheet = TRUE;
|
||||
|
||||
sprite->subpriority = subpriority;
|
||||
sprite->oam = *template->oam;
|
||||
sprite->anims = template->anims;
|
||||
sprite->affineAnims = template->affineAnims;
|
||||
sprite->template = template;
|
||||
sprite->callback = template->callback;
|
||||
sprite->x = x;
|
||||
sprite->y = y;
|
||||
|
||||
CalcCenterToCornerVec(sprite, sprite->oam.shape, sprite->oam.size, sprite->oam.affineMode);
|
||||
|
||||
if (template->tileTag == TAG_NONE)
|
||||
{
|
||||
s16 tileNum;
|
||||
sprite->images = template->images;
|
||||
tileNum = AllocSpriteTiles((u8)(sprite->images->size / TILE_SIZE_4BPP));
|
||||
if (tileNum == -1)
|
||||
{
|
||||
ResetSprite(sprite);
|
||||
return MAX_SPRITES;
|
||||
}
|
||||
sprite->oam.tileNum = tileNum;
|
||||
sprite->usingSheet = FALSE;
|
||||
sprite->sheetTileStart = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite->sheetTileStart = GetSpriteTileStartByTag(template->tileTag);
|
||||
SetSpriteSheetFrameTileNum(sprite);
|
||||
}
|
||||
|
||||
if (sprite->oam.affineMode & ST_OAM_AFFINE_ON_MASK)
|
||||
InitSpriteAffineAnim(sprite);
|
||||
|
||||
if (template->paletteTag != TAG_NONE)
|
||||
sprite->oam.paletteNum = IndexOfSpritePaletteTag(template->paletteTag);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
u8 CreateSpriteAndAnimate(const struct SpriteTemplate *template, s16 x, s16 y, u8 subpriority)
|
||||
{
|
||||
u8 i;
|
||||
|
||||
for (i = 0; i < MAX_SPRITES; i++)
|
||||
{
|
||||
struct Sprite *sprite = &gSprites[i];
|
||||
|
||||
if (!gSprites[i].inUse)
|
||||
{
|
||||
u8 index = CreateSpriteAt(i, template, x, y, subpriority);
|
||||
|
||||
if (index == MAX_SPRITES)
|
||||
return MAX_SPRITES;
|
||||
|
||||
gSprites[i].callback(sprite);
|
||||
|
||||
if (gSprites[i].inUse)
|
||||
AnimateSprite(sprite);
|
||||
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
return MAX_SPRITES;
|
||||
}
|
||||
|
||||
void DestroySprite(struct Sprite *sprite)
|
||||
{
|
||||
if (sprite->inUse)
|
||||
{
|
||||
if (!sprite->usingSheet)
|
||||
{
|
||||
u16 i;
|
||||
u16 tileEnd = (sprite->images->size / TILE_SIZE_4BPP) + sprite->oam.tileNum;
|
||||
for (i = sprite->oam.tileNum; i < tileEnd; i++)
|
||||
FREE_SPRITE_TILE(i);
|
||||
}
|
||||
ResetSprite(sprite);
|
||||
}
|
||||
}
|
||||
|
||||
void ResetOamRange(u8 a, u8 b)
|
||||
{
|
||||
u8 i;
|
||||
|
||||
for (i = a; i < b; i++)
|
||||
{
|
||||
gMain.oamBuffer[i] = *(struct OamData *)&gDummyOamData;
|
||||
}
|
||||
}
|
||||
|
||||
void LoadOam(void)
|
||||
{
|
||||
if (!gMain.oamLoadDisabled)
|
||||
CpuCopy32(gMain.oamBuffer, (void *)OAM, sizeof(gMain.oamBuffer));
|
||||
}
|
||||
|
||||
void ClearSpriteCopyRequests(void)
|
||||
{
|
||||
u8 i;
|
||||
|
||||
sShouldProcessSpriteCopyRequests = FALSE;
|
||||
sSpriteCopyRequestCount = 0;
|
||||
|
||||
for (i = 0; i < MAX_SPRITE_COPY_REQUESTS; i++)
|
||||
{
|
||||
sSpriteCopyRequests[i].src = 0;
|
||||
sSpriteCopyRequests[i].dest = 0;
|
||||
sSpriteCopyRequests[i].size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ResetOamMatrices(void)
|
||||
{
|
||||
u8 i;
|
||||
for (i = 0; i < OAM_MATRIX_COUNT; i++)
|
||||
{
|
||||
// set to identity matrix
|
||||
gOamMatrices[i].a = 0x0100;
|
||||
gOamMatrices[i].b = 0x0000;
|
||||
gOamMatrices[i].c = 0x0000;
|
||||
gOamMatrices[i].d = 0x0100;
|
||||
}
|
||||
}
|
||||
|
||||
void SetOamMatrix(u8 matrixNum, u16 a, u16 b, u16 c, u16 d)
|
||||
{
|
||||
gOamMatrices[matrixNum].a = a;
|
||||
gOamMatrices[matrixNum].b = b;
|
||||
gOamMatrices[matrixNum].c = c;
|
||||
gOamMatrices[matrixNum].d = d;
|
||||
}
|
||||
|
||||
void ResetSprite(struct Sprite *sprite)
|
||||
{
|
||||
*sprite = sDummySprite;
|
||||
}
|
||||
|
||||
void CalcCenterToCornerVec(struct Sprite *sprite, u8 shape, u8 size, u8 affineMode)
|
||||
{
|
||||
u8 x = sCenterToCornerVecTable[shape][size][0];
|
||||
u8 y = sCenterToCornerVecTable[shape][size][1];
|
||||
|
||||
if (affineMode & ST_OAM_AFFINE_DOUBLE_MASK)
|
||||
{
|
||||
x *= 2;
|
||||
y *= 2;
|
||||
}
|
||||
|
||||
sprite->centerToCornerVecX = x;
|
||||
sprite->centerToCornerVecY = y;
|
||||
}
|
||||
|
||||
s16 AllocSpriteTiles(u16 tileCount)
|
||||
{
|
||||
u16 i;
|
||||
s16 start;
|
||||
u16 numTilesFound;
|
||||
|
||||
if (tileCount == 0)
|
||||
{
|
||||
// Free all unreserved tiles if the tile count is 0.
|
||||
for (i = gReservedSpriteTileCount; i < TOTAL_OBJ_TILE_COUNT; i++)
|
||||
FREE_SPRITE_TILE(i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
i = gReservedSpriteTileCount;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
while (SPRITE_TILE_IS_ALLOCATED(i))
|
||||
{
|
||||
i++;
|
||||
|
||||
if (i == TOTAL_OBJ_TILE_COUNT)
|
||||
return -1;
|
||||
}
|
||||
|
||||
start = i;
|
||||
numTilesFound = 1;
|
||||
|
||||
while (numTilesFound != tileCount)
|
||||
{
|
||||
i++;
|
||||
|
||||
if (i == TOTAL_OBJ_TILE_COUNT)
|
||||
return -1;
|
||||
|
||||
if (!SPRITE_TILE_IS_ALLOCATED(i))
|
||||
numTilesFound++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (numTilesFound == tileCount)
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = start; i < tileCount + start; i++)
|
||||
ALLOC_SPRITE_TILE(i);
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
u8 SpriteTileAllocBitmapOp(u16 bit, u8 op)
|
||||
{
|
||||
u8 index = bit / 8;
|
||||
u8 shift = bit % 8;
|
||||
u8 val = bit % 8;
|
||||
u8 retVal = 0;
|
||||
|
||||
if (op == 0)
|
||||
{
|
||||
val = ~(1 << val);
|
||||
sSpriteTileAllocBitmap[index] &= val;
|
||||
}
|
||||
else if (op == 1)
|
||||
{
|
||||
val = (1 << val);
|
||||
sSpriteTileAllocBitmap[index] |= val;
|
||||
}
|
||||
else
|
||||
{
|
||||
retVal = 1 << shift;
|
||||
retVal &= sSpriteTileAllocBitmap[index];
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
void SpriteCallbackDummy(struct Sprite *sprite)
|
||||
{
|
||||
}
|
||||
|
||||
void ProcessSpriteCopyRequests(void)
|
||||
{
|
||||
if (sShouldProcessSpriteCopyRequests)
|
||||
{
|
||||
u8 i = 0;
|
||||
|
||||
while (sSpriteCopyRequestCount > 0)
|
||||
{
|
||||
CpuCopy16(sSpriteCopyRequests[i].src, sSpriteCopyRequests[i].dest, sSpriteCopyRequests[i].size);
|
||||
sSpriteCopyRequestCount--;
|
||||
i++;
|
||||
}
|
||||
|
||||
sShouldProcessSpriteCopyRequests = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
void RequestSpriteFrameImageCopy(u16 index, u16 tileNum, const struct SpriteFrameImage *images)
|
||||
{
|
||||
if (sSpriteCopyRequestCount < MAX_SPRITE_COPY_REQUESTS)
|
||||
{
|
||||
sSpriteCopyRequests[sSpriteCopyRequestCount].src = images[index].data;
|
||||
sSpriteCopyRequests[sSpriteCopyRequestCount].dest = (u8 *)OBJ_VRAM0 + TILE_SIZE_4BPP * tileNum;
|
||||
sSpriteCopyRequests[sSpriteCopyRequestCount].size = images[index].size;
|
||||
sSpriteCopyRequestCount++;
|
||||
}
|
||||
}
|
||||
|
||||
void RequestSpriteCopy(const u8 *src, u8 *dest, u16 size)
|
||||
{
|
||||
if (sSpriteCopyRequestCount < MAX_SPRITE_COPY_REQUESTS)
|
||||
{
|
||||
sSpriteCopyRequests[sSpriteCopyRequestCount].src = src;
|
||||
sSpriteCopyRequests[sSpriteCopyRequestCount].dest = dest;
|
||||
sSpriteCopyRequests[sSpriteCopyRequestCount].size = size;
|
||||
sSpriteCopyRequestCount++;
|
||||
}
|
||||
}
|
||||
|
||||
void CopyFromSprites(u8 *dest)
|
||||
{
|
||||
u32 i;
|
||||
u8 *src = (u8 *)gSprites;
|
||||
for (i = 0; i < sizeof(struct Sprite) * MAX_SPRITES; i++)
|
||||
{
|
||||
*dest = *src;
|
||||
dest++;
|
||||
src++;
|
||||
}
|
||||
}
|
||||
|
||||
void CopyToSprites(u8 *src)
|
||||
{
|
||||
u32 i;
|
||||
u8 *dest = (u8 *)gSprites;
|
||||
for (i = 0; i < sizeof(struct Sprite) * MAX_SPRITES; i++)
|
||||
{
|
||||
*dest = *src;
|
||||
src++;
|
||||
dest++;
|
||||
}
|
||||
}
|
||||
|
||||
void ResetAllSprites(void)
|
||||
{
|
||||
u8 i;
|
||||
|
||||
for (i = 0; i < MAX_SPRITES; i++)
|
||||
{
|
||||
ResetSprite(&gSprites[i]);
|
||||
sSpriteOrder[i] = i;
|
||||
}
|
||||
|
||||
ResetSprite(&gSprites[i]);
|
||||
}
|
||||
|
||||
void FreeSpriteTiles(struct Sprite *sprite)
|
||||
{
|
||||
// UB: template pointer may point to freed temporary storage
|
||||
#ifdef UBFIX
|
||||
if (!sprite || !sprite->template)
|
||||
return;
|
||||
#endif
|
||||
|
||||
if (sprite->template->tileTag != TAG_NONE)
|
||||
FreeSpriteTilesByTag(sprite->template->tileTag);
|
||||
}
|
||||
|
||||
void FreeSpritePalette(struct Sprite *sprite)
|
||||
{
|
||||
// UB: template pointer may point to freed temporary storage
|
||||
#ifdef UBFIX
|
||||
if (!sprite || !sprite->template)
|
||||
return;
|
||||
#endif
|
||||
|
||||
FreeSpritePaletteByTag(sprite->template->paletteTag);
|
||||
}
|
||||
|
||||
void FreeSpriteOamMatrix(struct Sprite *sprite)
|
||||
{
|
||||
if (sprite->oam.affineMode & ST_OAM_AFFINE_ON_MASK)
|
||||
{
|
||||
FreeOamMatrix(sprite->oam.matrixNum);
|
||||
sprite->oam.affineMode = ST_OAM_AFFINE_OFF;
|
||||
}
|
||||
}
|
||||
|
||||
void DestroySpriteAndFreeResources(struct Sprite *sprite)
|
||||
{
|
||||
FreeSpriteTiles(sprite);
|
||||
FreeSpritePalette(sprite);
|
||||
FreeSpriteOamMatrix(sprite);
|
||||
DestroySprite(sprite);
|
||||
}
|
||||
|
||||
void AnimateSprite(struct Sprite *sprite)
|
||||
{
|
||||
sAnimFuncs[sprite->animBeginning](sprite);
|
||||
|
||||
if (!gAffineAnimsDisabled)
|
||||
sAffineAnimFuncs[sprite->affineAnimBeginning](sprite);
|
||||
}
|
||||
|
||||
void BeginAnim(struct Sprite *sprite)
|
||||
{
|
||||
s16 imageValue;
|
||||
u8 duration;
|
||||
u8 hFlip;
|
||||
u8 vFlip;
|
||||
|
||||
sprite->animCmdIndex = 0;
|
||||
sprite->animEnded = FALSE;
|
||||
sprite->animLoopCounter = 0;
|
||||
imageValue = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.imageValue;
|
||||
|
||||
if (imageValue != -1)
|
||||
{
|
||||
sprite->animBeginning = FALSE;
|
||||
duration = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.duration;
|
||||
hFlip = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.hFlip;
|
||||
vFlip = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.vFlip;
|
||||
|
||||
if (duration)
|
||||
duration--;
|
||||
|
||||
sprite->animDelayCounter = duration;
|
||||
|
||||
if (!(sprite->oam.affineMode & ST_OAM_AFFINE_ON_MASK))
|
||||
SetSpriteOamFlipBits(sprite, hFlip, vFlip);
|
||||
|
||||
if (sprite->usingSheet)
|
||||
sprite->oam.tileNum = sprite->sheetTileStart + imageValue;
|
||||
else
|
||||
RequestSpriteFrameImageCopy(imageValue, sprite->oam.tileNum, sprite->images);
|
||||
}
|
||||
}
|
||||
|
||||
void ContinueAnim(struct Sprite *sprite)
|
||||
{
|
||||
if (sprite->animDelayCounter)
|
||||
{
|
||||
u8 hFlip;
|
||||
u8 vFlip;
|
||||
DecrementAnimDelayCounter(sprite);
|
||||
hFlip = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.hFlip;
|
||||
vFlip = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.vFlip;
|
||||
if (!(sprite->oam.affineMode & ST_OAM_AFFINE_ON_MASK))
|
||||
SetSpriteOamFlipBits(sprite, hFlip, vFlip);
|
||||
}
|
||||
else if (!sprite->animPaused)
|
||||
{
|
||||
s16 type;
|
||||
s16 funcIndex;
|
||||
sprite->animCmdIndex++;
|
||||
type = sprite->anims[sprite->animNum][sprite->animCmdIndex].type;
|
||||
funcIndex = 3;
|
||||
if (type < 0)
|
||||
funcIndex = type + 3;
|
||||
sAnimCmdFuncs[funcIndex](sprite);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimCmd_frame(struct Sprite *sprite)
|
||||
{
|
||||
s16 imageValue;
|
||||
u8 duration;
|
||||
u8 hFlip;
|
||||
u8 vFlip;
|
||||
|
||||
imageValue = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.imageValue;
|
||||
duration = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.duration;
|
||||
hFlip = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.hFlip;
|
||||
vFlip = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.vFlip;
|
||||
|
||||
if (duration)
|
||||
duration--;
|
||||
|
||||
sprite->animDelayCounter = duration;
|
||||
|
||||
if (!(sprite->oam.affineMode & ST_OAM_AFFINE_ON_MASK))
|
||||
SetSpriteOamFlipBits(sprite, hFlip, vFlip);
|
||||
|
||||
if (sprite->usingSheet)
|
||||
sprite->oam.tileNum = sprite->sheetTileStart + imageValue;
|
||||
else
|
||||
RequestSpriteFrameImageCopy(imageValue, sprite->oam.tileNum, sprite->images);
|
||||
}
|
||||
|
||||
void AnimCmd_end(struct Sprite *sprite)
|
||||
{
|
||||
sprite->animCmdIndex--;
|
||||
sprite->animEnded = TRUE;
|
||||
}
|
||||
|
||||
void AnimCmd_jump(struct Sprite *sprite)
|
||||
{
|
||||
s16 imageValue;
|
||||
u8 duration;
|
||||
u8 hFlip;
|
||||
u8 vFlip;
|
||||
|
||||
sprite->animCmdIndex = sprite->anims[sprite->animNum][sprite->animCmdIndex].jump.target;
|
||||
|
||||
imageValue = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.imageValue;
|
||||
duration = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.duration;
|
||||
hFlip = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.hFlip;
|
||||
vFlip = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.vFlip;
|
||||
|
||||
if (duration)
|
||||
duration--;
|
||||
|
||||
sprite->animDelayCounter = duration;
|
||||
|
||||
if (!(sprite->oam.affineMode & ST_OAM_AFFINE_ON_MASK))
|
||||
SetSpriteOamFlipBits(sprite, hFlip, vFlip);
|
||||
|
||||
if (sprite->usingSheet)
|
||||
sprite->oam.tileNum = sprite->sheetTileStart + imageValue;
|
||||
else
|
||||
RequestSpriteFrameImageCopy(imageValue, sprite->oam.tileNum, sprite->images);
|
||||
}
|
||||
|
||||
void AnimCmd_loop(struct Sprite *sprite)
|
||||
{
|
||||
if (sprite->animLoopCounter)
|
||||
ContinueAnimLoop(sprite);
|
||||
else
|
||||
BeginAnimLoop(sprite);
|
||||
}
|
||||
|
||||
void BeginAnimLoop(struct Sprite *sprite)
|
||||
{
|
||||
sprite->animLoopCounter = sprite->anims[sprite->animNum][sprite->animCmdIndex].loop.count;
|
||||
JumpToTopOfAnimLoop(sprite);
|
||||
ContinueAnim(sprite);
|
||||
}
|
||||
|
||||
void ContinueAnimLoop(struct Sprite *sprite)
|
||||
{
|
||||
sprite->animLoopCounter--;
|
||||
JumpToTopOfAnimLoop(sprite);
|
||||
ContinueAnim(sprite);
|
||||
}
|
||||
|
||||
void JumpToTopOfAnimLoop(struct Sprite *sprite)
|
||||
{
|
||||
if (sprite->animLoopCounter)
|
||||
{
|
||||
sprite->animCmdIndex--;
|
||||
|
||||
while (sprite->anims[sprite->animNum][sprite->animCmdIndex - 1].type != -3)
|
||||
{
|
||||
if (sprite->animCmdIndex == 0)
|
||||
break;
|
||||
sprite->animCmdIndex--;
|
||||
}
|
||||
|
||||
sprite->animCmdIndex--;
|
||||
}
|
||||
}
|
||||
|
||||
void BeginAffineAnim(struct Sprite *sprite)
|
||||
{
|
||||
if ((sprite->oam.affineMode & ST_OAM_AFFINE_ON_MASK) && sprite->affineAnims[0][0].type != 32767)
|
||||
{
|
||||
struct AffineAnimFrameCmd frameCmd;
|
||||
u8 matrixNum = GetSpriteMatrixNum(sprite);
|
||||
AffineAnimStateRestartAnim(matrixNum);
|
||||
GetAffineAnimFrame(matrixNum, sprite, &frameCmd);
|
||||
sprite->affineAnimBeginning = FALSE;
|
||||
sprite->affineAnimEnded = FALSE;
|
||||
ApplyAffineAnimFrame(matrixNum, &frameCmd);
|
||||
sAffineAnimStates[matrixNum].delayCounter = frameCmd.duration;
|
||||
if (sprite->anchored)
|
||||
UpdateSpriteMatrixAnchorPos(sprite, sprite->sAnchorX, sprite->sAnchorY);
|
||||
}
|
||||
}
|
||||
|
||||
void ContinueAffineAnim(struct Sprite *sprite)
|
||||
{
|
||||
if (sprite->oam.affineMode & ST_OAM_AFFINE_ON_MASK)
|
||||
{
|
||||
u8 matrixNum = GetSpriteMatrixNum(sprite);
|
||||
|
||||
if (sAffineAnimStates[matrixNum].delayCounter)
|
||||
AffineAnimDelay(matrixNum, sprite);
|
||||
else if (sprite->affineAnimPaused)
|
||||
return;
|
||||
else
|
||||
{
|
||||
s16 type;
|
||||
s16 funcIndex;
|
||||
sAffineAnimStates[matrixNum].animCmdIndex++;
|
||||
type = sprite->affineAnims[sAffineAnimStates[matrixNum].animNum][sAffineAnimStates[matrixNum].animCmdIndex].type;
|
||||
funcIndex = 3;
|
||||
if (type >= 32765)
|
||||
funcIndex = type - 32765;
|
||||
sAffineAnimCmdFuncs[funcIndex](matrixNum, sprite);
|
||||
}
|
||||
if (sprite->anchored)
|
||||
UpdateSpriteMatrixAnchorPos(sprite, sprite->sAnchorX, sprite->sAnchorY);
|
||||
}
|
||||
}
|
||||
|
||||
void AffineAnimDelay(u8 matrixNum, struct Sprite *sprite)
|
||||
{
|
||||
if (!DecrementAffineAnimDelayCounter(sprite, matrixNum))
|
||||
{
|
||||
struct AffineAnimFrameCmd frameCmd;
|
||||
GetAffineAnimFrame(matrixNum, sprite, &frameCmd);
|
||||
ApplyAffineAnimFrameRelativeAndUpdateMatrix(matrixNum, &frameCmd);
|
||||
}
|
||||
}
|
||||
|
||||
void AffineAnimCmd_loop(u8 matrixNum, struct Sprite *sprite)
|
||||
{
|
||||
if (sAffineAnimStates[matrixNum].loopCounter)
|
||||
ContinueAffineAnimLoop(matrixNum, sprite);
|
||||
else
|
||||
BeginAffineAnimLoop(matrixNum, sprite);
|
||||
}
|
||||
|
||||
void BeginAffineAnimLoop(u8 matrixNum, struct Sprite *sprite)
|
||||
{
|
||||
sAffineAnimStates[matrixNum].loopCounter = sprite->affineAnims[sAffineAnimStates[matrixNum].animNum][sAffineAnimStates[matrixNum].animCmdIndex].loop.count;
|
||||
JumpToTopOfAffineAnimLoop(matrixNum, sprite);
|
||||
ContinueAffineAnim(sprite);
|
||||
}
|
||||
|
||||
void ContinueAffineAnimLoop(u8 matrixNum, struct Sprite *sprite)
|
||||
{
|
||||
sAffineAnimStates[matrixNum].loopCounter--;
|
||||
JumpToTopOfAffineAnimLoop(matrixNum, sprite);
|
||||
ContinueAffineAnim(sprite);
|
||||
}
|
||||
|
||||
void JumpToTopOfAffineAnimLoop(u8 matrixNum, struct Sprite *sprite)
|
||||
{
|
||||
if (sAffineAnimStates[matrixNum].loopCounter)
|
||||
{
|
||||
sAffineAnimStates[matrixNum].animCmdIndex--;
|
||||
|
||||
while (sprite->affineAnims[sAffineAnimStates[matrixNum].animNum][sAffineAnimStates[matrixNum].animCmdIndex - 1].type != 32765)
|
||||
{
|
||||
if (sAffineAnimStates[matrixNum].animCmdIndex == 0)
|
||||
break;
|
||||
sAffineAnimStates[matrixNum].animCmdIndex--;
|
||||
}
|
||||
|
||||
sAffineAnimStates[matrixNum].animCmdIndex--;
|
||||
}
|
||||
}
|
||||
|
||||
void AffineAnimCmd_jump(u8 matrixNum, struct Sprite *sprite)
|
||||
{
|
||||
struct AffineAnimFrameCmd frameCmd;
|
||||
sAffineAnimStates[matrixNum].animCmdIndex = sprite->affineAnims[sAffineAnimStates[matrixNum].animNum][sAffineAnimStates[matrixNum].animCmdIndex].jump.target;
|
||||
GetAffineAnimFrame(matrixNum, sprite, &frameCmd);
|
||||
ApplyAffineAnimFrame(matrixNum, &frameCmd);
|
||||
sAffineAnimStates[matrixNum].delayCounter = frameCmd.duration;
|
||||
}
|
||||
|
||||
void AffineAnimCmd_end(u8 matrixNum, struct Sprite *sprite)
|
||||
{
|
||||
struct AffineAnimFrameCmd dummyFrameCmd = {0};
|
||||
sprite->affineAnimEnded = TRUE;
|
||||
sAffineAnimStates[matrixNum].animCmdIndex--;
|
||||
ApplyAffineAnimFrameRelativeAndUpdateMatrix(matrixNum, &dummyFrameCmd);
|
||||
}
|
||||
|
||||
void AffineAnimCmd_frame(u8 matrixNum, struct Sprite *sprite)
|
||||
{
|
||||
struct AffineAnimFrameCmd frameCmd;
|
||||
GetAffineAnimFrame(matrixNum, sprite, &frameCmd);
|
||||
ApplyAffineAnimFrame(matrixNum, &frameCmd);
|
||||
sAffineAnimStates[matrixNum].delayCounter = frameCmd.duration;
|
||||
}
|
||||
|
||||
void CopyOamMatrix(u8 destMatrixIndex, struct OamMatrix *srcMatrix)
|
||||
{
|
||||
gOamMatrices[destMatrixIndex].a = srcMatrix->a;
|
||||
gOamMatrices[destMatrixIndex].b = srcMatrix->b;
|
||||
gOamMatrices[destMatrixIndex].c = srcMatrix->c;
|
||||
gOamMatrices[destMatrixIndex].d = srcMatrix->d;
|
||||
}
|
||||
|
||||
u8 GetSpriteMatrixNum(struct Sprite *sprite)
|
||||
{
|
||||
u8 matrixNum = 0;
|
||||
if (sprite->oam.affineMode & ST_OAM_AFFINE_ON_MASK)
|
||||
matrixNum = sprite->oam.matrixNum;
|
||||
return matrixNum;
|
||||
}
|
||||
|
||||
// Used to shift a sprite's position as it scales.
|
||||
// Only used by the minigame countdown, so that for instance the numbers don't slide up as they squish down before jumping.
|
||||
void SetSpriteMatrixAnchor(struct Sprite* sprite, s16 x, s16 y)
|
||||
{
|
||||
sprite->sAnchorX = x;
|
||||
sprite->sAnchorY = y;
|
||||
sprite->anchored = TRUE;
|
||||
}
|
||||
|
||||
static s32 GetAnchorCoord(s32 a0, s32 a1, s32 coord)
|
||||
{
|
||||
s32 subResult, var1;
|
||||
|
||||
subResult = a1 - a0;
|
||||
if (subResult < 0)
|
||||
var1 = -(subResult) >> 9;
|
||||
else
|
||||
var1 = -(subResult >> 9);
|
||||
return coord - ((u32)(coord * a1) / (u32)(a0) + var1);
|
||||
}
|
||||
|
||||
static void UpdateSpriteMatrixAnchorPos(struct Sprite *sprite, s32 x, s32 y)
|
||||
{
|
||||
s32 dimension, var1, var2;
|
||||
|
||||
u32 matrixNum = sprite->oam.matrixNum;
|
||||
if (x != NO_ANCHOR)
|
||||
{
|
||||
dimension = sOamDimensions32[sprite->oam.shape][sprite->oam.size].width;
|
||||
var1 = dimension << 8;
|
||||
var2 = (dimension << 16) / gOamMatrices[matrixNum].a;
|
||||
sprite->x2 = GetAnchorCoord(var1, var2, x);
|
||||
}
|
||||
if (y != NO_ANCHOR)
|
||||
{
|
||||
dimension = sOamDimensions32[sprite->oam.shape][sprite->oam.size].height;
|
||||
var1 = dimension << 8;
|
||||
var2 = (dimension << 16) / gOamMatrices[matrixNum].d;
|
||||
sprite->y2 = GetAnchorCoord(var1, var2, y);
|
||||
}
|
||||
}
|
||||
|
||||
void SetSpriteOamFlipBits(struct Sprite *sprite, u8 hFlip, u8 vFlip)
|
||||
{
|
||||
sprite->oam.matrixNum &= 0x7;
|
||||
sprite->oam.matrixNum |= (((hFlip ^ sprite->hFlip) & 1) << 3);
|
||||
sprite->oam.matrixNum |= (((vFlip ^ sprite->vFlip) & 1) << 4);
|
||||
}
|
||||
|
||||
void AffineAnimStateRestartAnim(u8 matrixNum)
|
||||
{
|
||||
sAffineAnimStates[matrixNum].animCmdIndex = 0;
|
||||
sAffineAnimStates[matrixNum].delayCounter = 0;
|
||||
sAffineAnimStates[matrixNum].loopCounter = 0;
|
||||
}
|
||||
|
||||
void AffineAnimStateStartAnim(u8 matrixNum, u8 animNum)
|
||||
{
|
||||
sAffineAnimStates[matrixNum].animNum = animNum;
|
||||
sAffineAnimStates[matrixNum].animCmdIndex = 0;
|
||||
sAffineAnimStates[matrixNum].delayCounter = 0;
|
||||
sAffineAnimStates[matrixNum].loopCounter = 0;
|
||||
sAffineAnimStates[matrixNum].xScale = 0x0100;
|
||||
sAffineAnimStates[matrixNum].yScale = 0x0100;
|
||||
sAffineAnimStates[matrixNum].rotation = 0;
|
||||
}
|
||||
|
||||
void AffineAnimStateReset(u8 matrixNum)
|
||||
{
|
||||
sAffineAnimStates[matrixNum].animNum = 0;
|
||||
sAffineAnimStates[matrixNum].animCmdIndex = 0;
|
||||
sAffineAnimStates[matrixNum].delayCounter = 0;
|
||||
sAffineAnimStates[matrixNum].loopCounter = 0;
|
||||
sAffineAnimStates[matrixNum].xScale = 0x0100;
|
||||
sAffineAnimStates[matrixNum].yScale = 0x0100;
|
||||
sAffineAnimStates[matrixNum].rotation = 0;
|
||||
}
|
||||
|
||||
void ApplyAffineAnimFrameAbsolute(u8 matrixNum, struct AffineAnimFrameCmd *frameCmd)
|
||||
{
|
||||
sAffineAnimStates[matrixNum].xScale = frameCmd->xScale;
|
||||
sAffineAnimStates[matrixNum].yScale = frameCmd->yScale;
|
||||
sAffineAnimStates[matrixNum].rotation = frameCmd->rotation << 8;
|
||||
}
|
||||
|
||||
void DecrementAnimDelayCounter(struct Sprite *sprite)
|
||||
{
|
||||
if (!sprite->animPaused)
|
||||
sprite->animDelayCounter--;
|
||||
}
|
||||
|
||||
bool8 DecrementAffineAnimDelayCounter(struct Sprite *sprite, u8 matrixNum)
|
||||
{
|
||||
if (!sprite->affineAnimPaused)
|
||||
--sAffineAnimStates[matrixNum].delayCounter;
|
||||
return sprite->affineAnimPaused;
|
||||
}
|
||||
|
||||
void ApplyAffineAnimFrameRelativeAndUpdateMatrix(u8 matrixNum, struct AffineAnimFrameCmd *frameCmd)
|
||||
{
|
||||
struct ObjAffineSrcData srcData;
|
||||
struct OamMatrix matrix;
|
||||
sAffineAnimStates[matrixNum].xScale += frameCmd->xScale;
|
||||
sAffineAnimStates[matrixNum].yScale += frameCmd->yScale;
|
||||
sAffineAnimStates[matrixNum].rotation = (sAffineAnimStates[matrixNum].rotation + (frameCmd->rotation << 8)) & ~0xFF;
|
||||
srcData.xScale = ConvertScaleParam(sAffineAnimStates[matrixNum].xScale);
|
||||
srcData.yScale = ConvertScaleParam(sAffineAnimStates[matrixNum].yScale);
|
||||
srcData.rotation = sAffineAnimStates[matrixNum].rotation;
|
||||
ObjAffineSet(&srcData, &matrix, 1, 2);
|
||||
CopyOamMatrix(matrixNum, &matrix);
|
||||
}
|
||||
|
||||
s16 ConvertScaleParam(s16 scale)
|
||||
{
|
||||
s32 val = 0x10000;
|
||||
return SAFE_DIV(val, scale);
|
||||
}
|
||||
|
||||
void GetAffineAnimFrame(u8 matrixNum, struct Sprite *sprite, struct AffineAnimFrameCmd *frameCmd)
|
||||
{
|
||||
frameCmd->xScale = sprite->affineAnims[sAffineAnimStates[matrixNum].animNum][sAffineAnimStates[matrixNum].animCmdIndex].frame.xScale;
|
||||
frameCmd->yScale = sprite->affineAnims[sAffineAnimStates[matrixNum].animNum][sAffineAnimStates[matrixNum].animCmdIndex].frame.yScale;
|
||||
frameCmd->rotation = sprite->affineAnims[sAffineAnimStates[matrixNum].animNum][sAffineAnimStates[matrixNum].animCmdIndex].frame.rotation;
|
||||
frameCmd->duration = sprite->affineAnims[sAffineAnimStates[matrixNum].animNum][sAffineAnimStates[matrixNum].animCmdIndex].frame.duration;
|
||||
}
|
||||
|
||||
void ApplyAffineAnimFrame(u8 matrixNum, struct AffineAnimFrameCmd *frameCmd)
|
||||
{
|
||||
struct AffineAnimFrameCmd dummyFrameCmd = {0};
|
||||
|
||||
if (frameCmd->duration)
|
||||
{
|
||||
frameCmd->duration--;
|
||||
ApplyAffineAnimFrameRelativeAndUpdateMatrix(matrixNum, frameCmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
ApplyAffineAnimFrameAbsolute(matrixNum, frameCmd);
|
||||
ApplyAffineAnimFrameRelativeAndUpdateMatrix(matrixNum, &dummyFrameCmd);
|
||||
}
|
||||
}
|
||||
|
||||
void StartSpriteAnim(struct Sprite *sprite, u8 animNum)
|
||||
{
|
||||
sprite->animNum = animNum;
|
||||
sprite->animBeginning = TRUE;
|
||||
sprite->animEnded = FALSE;
|
||||
}
|
||||
|
||||
void StartSpriteAnimIfDifferent(struct Sprite *sprite, u8 animNum)
|
||||
{
|
||||
if (sprite->animNum != animNum)
|
||||
StartSpriteAnim(sprite, animNum);
|
||||
}
|
||||
|
||||
void SeekSpriteAnim(struct Sprite *sprite, u8 animCmdIndex)
|
||||
{
|
||||
u8 temp = sprite->animPaused;
|
||||
sprite->animCmdIndex = animCmdIndex - 1;
|
||||
sprite->animDelayCounter = 0;
|
||||
sprite->animBeginning = FALSE;
|
||||
sprite->animEnded = FALSE;
|
||||
sprite->animPaused = FALSE;
|
||||
ContinueAnim(sprite);
|
||||
if (sprite->animDelayCounter)
|
||||
sprite->animDelayCounter++;
|
||||
sprite->animPaused = temp;
|
||||
}
|
||||
|
||||
void StartSpriteAffineAnim(struct Sprite *sprite, u8 animNum)
|
||||
{
|
||||
u8 matrixNum = GetSpriteMatrixNum(sprite);
|
||||
AffineAnimStateStartAnim(matrixNum, animNum);
|
||||
sprite->affineAnimBeginning = TRUE;
|
||||
sprite->affineAnimEnded = FALSE;
|
||||
}
|
||||
|
||||
void StartSpriteAffineAnimIfDifferent(struct Sprite *sprite, u8 animNum)
|
||||
{
|
||||
u8 matrixNum = GetSpriteMatrixNum(sprite);
|
||||
if (sAffineAnimStates[matrixNum].animNum != animNum)
|
||||
StartSpriteAffineAnim(sprite, animNum);
|
||||
}
|
||||
|
||||
void ChangeSpriteAffineAnim(struct Sprite *sprite, u8 animNum)
|
||||
{
|
||||
u8 matrixNum = GetSpriteMatrixNum(sprite);
|
||||
sAffineAnimStates[matrixNum].animNum = animNum;
|
||||
sprite->affineAnimBeginning = TRUE;
|
||||
sprite->affineAnimEnded = FALSE;
|
||||
}
|
||||
|
||||
void ChangeSpriteAffineAnimIfDifferent(struct Sprite *sprite, u8 animNum)
|
||||
{
|
||||
u8 matrixNum = GetSpriteMatrixNum(sprite);
|
||||
if (sAffineAnimStates[matrixNum].animNum != animNum)
|
||||
ChangeSpriteAffineAnim(sprite, animNum);
|
||||
}
|
||||
|
||||
void SetSpriteSheetFrameTileNum(struct Sprite *sprite)
|
||||
{
|
||||
if (sprite->usingSheet)
|
||||
{
|
||||
s16 tileOffset = sprite->anims[sprite->animNum][sprite->animCmdIndex].frame.imageValue;
|
||||
if (tileOffset < 0)
|
||||
tileOffset = 0;
|
||||
sprite->oam.tileNum = sprite->sheetTileStart + tileOffset;
|
||||
}
|
||||
}
|
||||
|
||||
void ResetAffineAnimData(void)
|
||||
{
|
||||
u8 i;
|
||||
|
||||
gAffineAnimsDisabled = FALSE;
|
||||
gOamMatrixAllocBitmap = 0;
|
||||
|
||||
ResetOamMatrices();
|
||||
|
||||
for (i = 0; i < OAM_MATRIX_COUNT; i++)
|
||||
AffineAnimStateReset(i);
|
||||
}
|
||||
|
||||
u8 AllocOamMatrix(void)
|
||||
{
|
||||
u8 i = 0;
|
||||
u32 bit = 1;
|
||||
u32 bitmap = gOamMatrixAllocBitmap;
|
||||
|
||||
while (i < OAM_MATRIX_COUNT)
|
||||
{
|
||||
if (!(bitmap & bit))
|
||||
{
|
||||
gOamMatrixAllocBitmap |= bit;
|
||||
return i;
|
||||
}
|
||||
|
||||
i++;
|
||||
bit <<= 1;
|
||||
}
|
||||
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
void FreeOamMatrix(u8 matrixNum)
|
||||
{
|
||||
u8 i = 0;
|
||||
u32 bit = 1;
|
||||
|
||||
while (i < matrixNum)
|
||||
{
|
||||
i++;
|
||||
bit <<= 1;
|
||||
}
|
||||
|
||||
gOamMatrixAllocBitmap &= ~bit;
|
||||
SetOamMatrix(matrixNum, 0x100, 0, 0, 0x100);
|
||||
}
|
||||
|
||||
void InitSpriteAffineAnim(struct Sprite *sprite)
|
||||
{
|
||||
u8 matrixNum = AllocOamMatrix();
|
||||
if (matrixNum != 0xFF)
|
||||
{
|
||||
CalcCenterToCornerVec(sprite, sprite->oam.shape, sprite->oam.size, sprite->oam.affineMode);
|
||||
sprite->oam.matrixNum = matrixNum;
|
||||
sprite->affineAnimBeginning = TRUE;
|
||||
AffineAnimStateReset(matrixNum);
|
||||
}
|
||||
}
|
||||
|
||||
void SetOamMatrixRotationScaling(u8 matrixNum, s16 xScale, s16 yScale, u16 rotation)
|
||||
{
|
||||
struct ObjAffineSrcData srcData;
|
||||
struct OamMatrix matrix;
|
||||
srcData.xScale = ConvertScaleParam(xScale);
|
||||
srcData.yScale = ConvertScaleParam(yScale);
|
||||
srcData.rotation = rotation;
|
||||
ObjAffineSet(&srcData, &matrix, 1, 2);
|
||||
CopyOamMatrix(matrixNum, &matrix);
|
||||
}
|
||||
|
||||
u16 LoadSpriteSheet(const struct SpriteSheet *sheet)
|
||||
{
|
||||
s16 tileStart = AllocSpriteTiles(sheet->size / TILE_SIZE_4BPP);
|
||||
|
||||
if (tileStart < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
AllocSpriteTileRange(sheet->tag, (u16)tileStart, sheet->size / TILE_SIZE_4BPP);
|
||||
CpuCopy16(sheet->data, (u8 *)OBJ_VRAM0 + TILE_SIZE_4BPP * tileStart, sheet->size);
|
||||
return (u16)tileStart;
|
||||
}
|
||||
}
|
||||
|
||||
void LoadSpriteSheets(const struct SpriteSheet *sheets)
|
||||
{
|
||||
u8 i;
|
||||
for (i = 0; sheets[i].data != NULL; i++)
|
||||
LoadSpriteSheet(&sheets[i]);
|
||||
}
|
||||
|
||||
void FreeSpriteTilesByTag(u16 tag)
|
||||
{
|
||||
u8 index = IndexOfSpriteTileTag(tag);
|
||||
if (index != 0xFF)
|
||||
{
|
||||
u16 i;
|
||||
u16 *rangeStarts;
|
||||
u16 *rangeCounts;
|
||||
u16 start;
|
||||
u16 count;
|
||||
rangeStarts = sSpriteTileRanges;
|
||||
start = rangeStarts[index * 2];
|
||||
rangeCounts = sSpriteTileRanges + 1;
|
||||
count = rangeCounts[index * 2];
|
||||
|
||||
for (i = start; i < start + count; i++)
|
||||
FREE_SPRITE_TILE(i);
|
||||
|
||||
sSpriteTileRangeTags[index] = TAG_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
void FreeSpriteTileRanges(void)
|
||||
{
|
||||
u8 i;
|
||||
|
||||
for (i = 0; i < MAX_SPRITES; i++)
|
||||
{
|
||||
sSpriteTileRangeTags[i] = TAG_NONE;
|
||||
SET_SPRITE_TILE_RANGE(i, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
u16 GetSpriteTileStartByTag(u16 tag)
|
||||
{
|
||||
u8 index = IndexOfSpriteTileTag(tag);
|
||||
if (index == 0xFF)
|
||||
return 0xFFFF;
|
||||
return sSpriteTileRanges[index * 2];
|
||||
}
|
||||
|
||||
u8 IndexOfSpriteTileTag(u16 tag)
|
||||
{
|
||||
u8 i;
|
||||
|
||||
for (i = 0; i < MAX_SPRITES; i++)
|
||||
if (sSpriteTileRangeTags[i] == tag)
|
||||
return i;
|
||||
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
u16 GetSpriteTileTagByTileStart(u16 start)
|
||||
{
|
||||
u8 i;
|
||||
|
||||
for (i = 0; i < MAX_SPRITES; i++)
|
||||
{
|
||||
if (sSpriteTileRangeTags[i] != TAG_NONE && sSpriteTileRanges[i * 2] == start)
|
||||
return sSpriteTileRangeTags[i];
|
||||
}
|
||||
|
||||
return TAG_NONE;
|
||||
}
|
||||
|
||||
void AllocSpriteTileRange(u16 tag, u16 start, u16 count)
|
||||
{
|
||||
u8 freeIndex = IndexOfSpriteTileTag(TAG_NONE);
|
||||
sSpriteTileRangeTags[freeIndex] = tag;
|
||||
SET_SPRITE_TILE_RANGE(freeIndex, start, count);
|
||||
}
|
||||
|
||||
void FreeAllSpritePalettes(void)
|
||||
{
|
||||
u8 i;
|
||||
gReservedSpritePaletteCount = 0;
|
||||
for (i = 0; i < 16; i++)
|
||||
sSpritePaletteTags[i] = TAG_NONE;
|
||||
}
|
||||
|
||||
u8 LoadSpritePalette(const struct SpritePalette *palette)
|
||||
{
|
||||
u8 index = IndexOfSpritePaletteTag(palette->tag);
|
||||
|
||||
if (index != 0xFF)
|
||||
return index;
|
||||
|
||||
index = IndexOfSpritePaletteTag(TAG_NONE);
|
||||
|
||||
if (index == 0xFF)
|
||||
{
|
||||
return 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
sSpritePaletteTags[index] = palette->tag;
|
||||
DoLoadSpritePalette(palette->data, index * 16);
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
void LoadSpritePalettes(const struct SpritePalette *palettes)
|
||||
{
|
||||
u8 i;
|
||||
for (i = 0; palettes[i].data != NULL; i++)
|
||||
if (LoadSpritePalette(&palettes[i]) == 0xFF)
|
||||
break;
|
||||
}
|
||||
|
||||
void DoLoadSpritePalette(const u16 *src, u16 paletteOffset)
|
||||
{
|
||||
LoadPalette(src, paletteOffset + 0x100, 32);
|
||||
}
|
||||
|
||||
u8 AllocSpritePalette(u16 tag)
|
||||
{
|
||||
u8 index = IndexOfSpritePaletteTag(TAG_NONE);
|
||||
if (index == 0xFF)
|
||||
{
|
||||
return 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
sSpritePaletteTags[index] = tag;
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
u8 IndexOfSpritePaletteTag(u16 tag)
|
||||
{
|
||||
u8 i;
|
||||
for (i = gReservedSpritePaletteCount; i < 16; i++)
|
||||
if (sSpritePaletteTags[i] == tag)
|
||||
return i;
|
||||
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
u16 GetSpritePaletteTagByPaletteNum(u8 paletteNum)
|
||||
{
|
||||
return sSpritePaletteTags[paletteNum];
|
||||
}
|
||||
|
||||
void FreeSpritePaletteByTag(u16 tag)
|
||||
{
|
||||
u8 index = IndexOfSpritePaletteTag(tag);
|
||||
if (index != 0xFF)
|
||||
sSpritePaletteTags[index] = TAG_NONE;
|
||||
}
|
||||
|
||||
void SetSubspriteTables(struct Sprite *sprite, const struct SubspriteTable *subspriteTables)
|
||||
{
|
||||
sprite->subspriteTables = subspriteTables;
|
||||
sprite->subspriteTableNum = 0;
|
||||
sprite->subspriteMode = SUBSPRITES_ON;
|
||||
}
|
||||
|
||||
bool8 AddSpriteToOamBuffer(struct Sprite *sprite, u8 *oamIndex)
|
||||
{
|
||||
if (*oamIndex >= gOamLimit)
|
||||
return 1;
|
||||
|
||||
if (!sprite->subspriteTables || sprite->subspriteMode == SUBSPRITES_OFF)
|
||||
{
|
||||
gMain.oamBuffer[*oamIndex] = sprite->oam;
|
||||
(*oamIndex)++;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AddSubspritesToOamBuffer(sprite, &gMain.oamBuffer[*oamIndex], oamIndex);
|
||||
}
|
||||
}
|
||||
|
||||
bool8 AddSubspritesToOamBuffer(struct Sprite *sprite, struct OamData *destOam, u8 *oamIndex)
|
||||
{
|
||||
const struct SubspriteTable *subspriteTable;
|
||||
struct OamData *oam;
|
||||
|
||||
if (*oamIndex >= gOamLimit)
|
||||
return 1;
|
||||
|
||||
subspriteTable = &sprite->subspriteTables[sprite->subspriteTableNum];
|
||||
oam = &sprite->oam;
|
||||
|
||||
if (!subspriteTable || !subspriteTable->subsprites)
|
||||
{
|
||||
*destOam = *oam;
|
||||
(*oamIndex)++;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
u16 tileNum;
|
||||
u16 baseX;
|
||||
u16 baseY;
|
||||
u8 subspriteCount;
|
||||
u8 hFlip;
|
||||
u8 vFlip;
|
||||
u8 i;
|
||||
|
||||
tileNum = oam->tileNum;
|
||||
subspriteCount = subspriteTable->subspriteCount;
|
||||
hFlip = ((s32)oam->matrixNum >> 3) & 1;
|
||||
vFlip = ((s32)oam->matrixNum >> 4) & 1;
|
||||
baseX = oam->x - sprite->centerToCornerVecX;
|
||||
baseY = oam->y - sprite->centerToCornerVecY;
|
||||
|
||||
for (i = 0; i < subspriteCount; i++, (*oamIndex)++)
|
||||
{
|
||||
u16 x;
|
||||
u16 y;
|
||||
|
||||
if (*oamIndex >= gOamLimit)
|
||||
return 1;
|
||||
|
||||
x = subspriteTable->subsprites[i].x;
|
||||
y = subspriteTable->subsprites[i].y;
|
||||
|
||||
if (hFlip)
|
||||
{
|
||||
s8 width = sOamDimensions[subspriteTable->subsprites[i].shape][subspriteTable->subsprites[i].size].width;
|
||||
s16 right = x;
|
||||
right += width;
|
||||
x = right;
|
||||
x = ~x + 1;
|
||||
}
|
||||
|
||||
if (vFlip)
|
||||
{
|
||||
s8 height = sOamDimensions[subspriteTable->subsprites[i].shape][subspriteTable->subsprites[i].size].height;
|
||||
s16 bottom = y;
|
||||
bottom += height;
|
||||
y = bottom;
|
||||
y = ~y + 1;
|
||||
}
|
||||
|
||||
destOam[i] = *oam;
|
||||
destOam[i].shape = subspriteTable->subsprites[i].shape;
|
||||
destOam[i].size = subspriteTable->subsprites[i].size;
|
||||
destOam[i].x = (s16)baseX + (s16)x;
|
||||
destOam[i].y = baseY + y;
|
||||
destOam[i].tileNum = tileNum + subspriteTable->subsprites[i].tileOffset;
|
||||
|
||||
if (sprite->subspriteMode != SUBSPRITES_IGNORE_PRIORITY)
|
||||
destOam[i].priority = subspriteTable->subsprites[i].priority;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
326
gflib/sprite.h
Normal file
326
gflib/sprite.h
Normal file
@ -0,0 +1,326 @@
|
||||
#ifndef GUARD_SPRITE_H
|
||||
#define GUARD_SPRITE_H
|
||||
|
||||
#define MAX_SPRITES 64
|
||||
#define SPRITE_NONE 0xFF
|
||||
#define TAG_NONE 0xFFFF
|
||||
|
||||
// Given to SetSpriteMatrixAnchor to skip anchoring one of the coords.
|
||||
#define NO_ANCHOR 0x800
|
||||
|
||||
struct SpriteSheet
|
||||
{
|
||||
const void *data; // Raw uncompressed pixel data
|
||||
u16 size;
|
||||
u16 tag;
|
||||
};
|
||||
|
||||
struct CompressedSpriteSheet
|
||||
{
|
||||
const u32 *data; // LZ77 compressed pixel data
|
||||
u16 size; // Uncompressed size of pixel data
|
||||
u16 tag;
|
||||
};
|
||||
|
||||
struct SpriteFrameImage
|
||||
{
|
||||
const void *data;
|
||||
u16 size;
|
||||
};
|
||||
|
||||
#define obj_frame_tiles(ptr) {.data = (u8 *)ptr, .size = sizeof ptr}
|
||||
|
||||
#define overworld_frame(ptr, width, height, frame) {.data = (u8 *)ptr + (width * height * frame * 64)/2, .size = (width * height * 64)/2}
|
||||
|
||||
struct SpritePalette
|
||||
{
|
||||
const u16 *data; // Raw uncompressed palette data
|
||||
u16 tag;
|
||||
};
|
||||
|
||||
struct CompressedSpritePalette
|
||||
{
|
||||
const u32 *data; // LZ77 compressed palette data
|
||||
u16 tag;
|
||||
};
|
||||
|
||||
struct AnimFrameCmd
|
||||
{
|
||||
// If the sprite has an array of images, this is the array index.
|
||||
// If the sprite has a sheet, this is the tile offset.
|
||||
u32 imageValue:16;
|
||||
|
||||
u32 duration:6;
|
||||
u32 hFlip:1;
|
||||
u32 vFlip:1;
|
||||
};
|
||||
|
||||
struct AnimLoopCmd
|
||||
{
|
||||
u32 type:16;
|
||||
u32 count:6;
|
||||
};
|
||||
|
||||
struct AnimJumpCmd
|
||||
{
|
||||
u32 type:16;
|
||||
u32 target:6;
|
||||
};
|
||||
|
||||
// The first halfword of this union specifies the type of command.
|
||||
// If it -2, then it is a jump command. If it is -1, then it is the end of the script.
|
||||
// Otherwise, it is the imageValue for a frame command.
|
||||
union AnimCmd
|
||||
{
|
||||
s16 type;
|
||||
struct AnimFrameCmd frame;
|
||||
struct AnimLoopCmd loop;
|
||||
struct AnimJumpCmd jump;
|
||||
};
|
||||
|
||||
#define ANIMCMD_FRAME(...) \
|
||||
{.frame = {__VA_ARGS__}}
|
||||
#define ANIMCMD_LOOP(_count) \
|
||||
{.loop = {.type = -3, .count = _count}}
|
||||
#define ANIMCMD_JUMP(_target) \
|
||||
{.jump = {.type = -2, .target = _target}}
|
||||
#define ANIMCMD_END \
|
||||
{.type = -1}
|
||||
|
||||
struct AffineAnimFrameCmd
|
||||
{
|
||||
s16 xScale;
|
||||
s16 yScale;
|
||||
u8 rotation;
|
||||
u8 duration;
|
||||
};
|
||||
|
||||
struct AffineAnimLoopCmd
|
||||
{
|
||||
s16 type;
|
||||
s16 count;
|
||||
};
|
||||
|
||||
struct AffineAnimJumpCmd
|
||||
{
|
||||
s16 type;
|
||||
u16 target;
|
||||
};
|
||||
|
||||
struct AffineAnimEndCmdAlt
|
||||
{
|
||||
s16 type;
|
||||
u16 val;
|
||||
};
|
||||
|
||||
union AffineAnimCmd
|
||||
{
|
||||
s16 type;
|
||||
struct AffineAnimFrameCmd frame;
|
||||
struct AffineAnimLoopCmd loop;
|
||||
struct AffineAnimJumpCmd jump;
|
||||
struct AffineAnimEndCmdAlt end; // unused in code
|
||||
};
|
||||
|
||||
#define AFFINEANIMCMDTYPE_LOOP 0x7FFD
|
||||
#define AFFINEANIMCMDTYPE_JUMP 0x7FFE
|
||||
#define AFFINEANIMCMDTYPE_END 0x7FFF
|
||||
|
||||
#define AFFINEANIMCMD_FRAME(_xScale, _yScale, _rotation, _duration) \
|
||||
{.frame = {.xScale = _xScale, .yScale = _yScale, .rotation = _rotation, .duration = _duration}}
|
||||
#define AFFINEANIMCMD_LOOP(_count) \
|
||||
{.loop = {.type = AFFINEANIMCMDTYPE_LOOP, .count = _count}}
|
||||
#define AFFINEANIMCMD_JUMP(_target) \
|
||||
{.jump = {.type = AFFINEANIMCMDTYPE_JUMP, .target = _target}}
|
||||
#define AFFINEANIMCMD_END \
|
||||
{.type = AFFINEANIMCMDTYPE_END}
|
||||
#define AFFINEANIMCMD_END_ALT(_val) \
|
||||
{.end = {.type = AFFINEANIMCMDTYPE_END, .val = _val}}
|
||||
|
||||
struct AffineAnimState
|
||||
{
|
||||
u8 animNum;
|
||||
u8 animCmdIndex;
|
||||
u8 delayCounter;
|
||||
u8 loopCounter;
|
||||
s16 xScale;
|
||||
s16 yScale;
|
||||
u16 rotation;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SUBSPRITES_OFF,
|
||||
SUBSPRITES_ON,
|
||||
SUBSPRITES_IGNORE_PRIORITY, // on but priority is ignored
|
||||
};
|
||||
|
||||
struct Subsprite
|
||||
{
|
||||
s8 x; // was u16 in R/S
|
||||
s8 y; // was u16 in R/S
|
||||
u16 shape:2;
|
||||
u16 size:2;
|
||||
u16 tileOffset:10;
|
||||
u16 priority:2;
|
||||
};
|
||||
|
||||
struct SubspriteTable
|
||||
{
|
||||
u8 subspriteCount;
|
||||
const struct Subsprite *subsprites;
|
||||
};
|
||||
|
||||
struct Sprite;
|
||||
|
||||
typedef void (*SpriteCallback)(struct Sprite *);
|
||||
|
||||
struct SpriteTemplate
|
||||
{
|
||||
u16 tileTag;
|
||||
u16 paletteTag;
|
||||
const struct OamData *oam;
|
||||
const union AnimCmd *const *anims;
|
||||
const struct SpriteFrameImage *images;
|
||||
const union AffineAnimCmd *const *affineAnims;
|
||||
SpriteCallback callback;
|
||||
};
|
||||
|
||||
// UB: template pointer is often used to point to temporary storage,
|
||||
// then later dereferenced after being freed. Usually this won't
|
||||
// be visible in-game, but this is (part of) what causes the item
|
||||
// icon palette to flicker when changing items in the bag.
|
||||
struct Sprite
|
||||
{
|
||||
/*0x00*/ struct OamData oam;
|
||||
/*0x08*/ const union AnimCmd *const *anims;
|
||||
/*0x0C*/ const struct SpriteFrameImage *images;
|
||||
/*0x10*/ const union AffineAnimCmd *const *affineAnims;
|
||||
/*0x14*/ const struct SpriteTemplate *template;
|
||||
/*0x18*/ const struct SubspriteTable *subspriteTables;
|
||||
/*0x1C*/ SpriteCallback callback;
|
||||
|
||||
/*0x20*/ s16 x, y;
|
||||
/*0x24*/ s16 x2, y2;
|
||||
/*0x28*/ s8 centerToCornerVecX;
|
||||
/*0x29*/ s8 centerToCornerVecY;
|
||||
|
||||
/*0x2A*/ u8 animNum;
|
||||
/*0x2B*/ u8 animCmdIndex;
|
||||
/*0x2C*/ u8 animDelayCounter:6;
|
||||
bool8 animPaused:1;
|
||||
bool8 affineAnimPaused:1;
|
||||
/*0x2D*/ u8 animLoopCounter;
|
||||
|
||||
// general purpose data fields
|
||||
/*0x2E*/ s16 data[8];
|
||||
|
||||
/*0x3E*/ bool16 inUse:1; //1
|
||||
bool16 coordOffsetEnabled:1; //2
|
||||
bool16 invisible:1; //4
|
||||
bool16 flags_3:1; //8
|
||||
bool16 flags_4:1; //0x10
|
||||
bool16 flags_5:1; //0x20
|
||||
bool16 flags_6:1; //0x40
|
||||
bool16 flags_7:1; //0x80
|
||||
/*0x3F*/ bool16 hFlip:1; //1
|
||||
bool16 vFlip:1; //2
|
||||
bool16 animBeginning:1; //4
|
||||
bool16 affineAnimBeginning:1; //8
|
||||
bool16 animEnded:1; //0x10
|
||||
bool16 affineAnimEnded:1; //0x20
|
||||
bool16 usingSheet:1; //0x40
|
||||
bool16 anchored:1; //0x80
|
||||
|
||||
/*0x40*/ u16 sheetTileStart;
|
||||
|
||||
/*0x42*/ u8 subspriteTableNum:6;
|
||||
u8 subspriteMode:2;
|
||||
|
||||
/*0x43*/ u8 subpriority;
|
||||
};
|
||||
|
||||
struct OamMatrix
|
||||
{
|
||||
s16 a;
|
||||
s16 b;
|
||||
s16 c;
|
||||
s16 d;
|
||||
};
|
||||
|
||||
extern const struct OamData gDummyOamData;
|
||||
extern const union AnimCmd *const gDummySpriteAnimTable[];
|
||||
extern const union AffineAnimCmd *const gDummySpriteAffineAnimTable[];
|
||||
extern const struct SpriteTemplate gDummySpriteTemplate;
|
||||
|
||||
extern u8 gReservedSpritePaletteCount;
|
||||
extern struct Sprite gSprites[];
|
||||
extern u8 gOamLimit;
|
||||
extern u16 gReservedSpriteTileCount;
|
||||
extern s16 gSpriteCoordOffsetX;
|
||||
extern s16 gSpriteCoordOffsetY;
|
||||
extern struct OamMatrix gOamMatrices[];
|
||||
extern bool8 gAffineAnimsDisabled;
|
||||
|
||||
void ResetSpriteData(void);
|
||||
void AnimateSprites(void);
|
||||
void BuildOamBuffer(void);
|
||||
u8 CreateSprite(const struct SpriteTemplate *template, s16 x, s16 y, u8 subpriority);
|
||||
u8 CreateSpriteAtEnd(const struct SpriteTemplate *template, s16 x, s16 y, u8 subpriority);
|
||||
u8 CreateInvisibleSprite(void (*callback)(struct Sprite *));
|
||||
u8 CreateSpriteAndAnimate(const struct SpriteTemplate *template, s16 x, s16 y, u8 subpriority);
|
||||
void DestroySprite(struct Sprite *sprite);
|
||||
void ResetOamRange(u8 a, u8 b);
|
||||
void LoadOam(void);
|
||||
void SetOamMatrix(u8 matrixNum, u16 a, u16 b, u16 c, u16 d);
|
||||
void CalcCenterToCornerVec(struct Sprite *sprite, u8 shape, u8 size, u8 affineMode);
|
||||
void SpriteCallbackDummy(struct Sprite *sprite);
|
||||
void ProcessSpriteCopyRequests(void);
|
||||
void RequestSpriteCopy(const u8 *src, u8 *dest, u16 size);
|
||||
void FreeSpriteTiles(struct Sprite *sprite);
|
||||
void FreeSpritePalette(struct Sprite *sprite);
|
||||
void FreeSpriteOamMatrix(struct Sprite *sprite);
|
||||
void DestroySpriteAndFreeResources(struct Sprite *sprite);
|
||||
void AnimateSprite(struct Sprite *sprite);
|
||||
void SetSpriteMatrixAnchor(struct Sprite* sprite, s16 x, s16 y);
|
||||
void StartSpriteAnim(struct Sprite *sprite, u8 animNum);
|
||||
void StartSpriteAnimIfDifferent(struct Sprite *sprite, u8 animNum);
|
||||
void SeekSpriteAnim(struct Sprite *sprite, u8 animCmdIndex);
|
||||
void StartSpriteAffineAnim(struct Sprite *sprite, u8 animNum);
|
||||
void StartSpriteAffineAnimIfDifferent(struct Sprite *sprite, u8 animNum);
|
||||
void ChangeSpriteAffineAnim(struct Sprite *sprite, u8 animNum);
|
||||
void ChangeSpriteAffineAnimIfDifferent(struct Sprite *sprite, u8 animNum);
|
||||
void SetSpriteSheetFrameTileNum(struct Sprite *sprite);
|
||||
u8 AllocOamMatrix(void);
|
||||
void FreeOamMatrix(u8 matrixNum);
|
||||
void InitSpriteAffineAnim(struct Sprite *sprite);
|
||||
void SetOamMatrixRotationScaling(u8 matrixNum, s16 xScale, s16 yScale, u16 rotation);
|
||||
u16 LoadSpriteSheet(const struct SpriteSheet *sheet);
|
||||
void LoadSpriteSheets(const struct SpriteSheet *sheets);
|
||||
u16 AllocTilesForSpriteSheet(struct SpriteSheet *sheet);
|
||||
void AllocTilesForSpriteSheets(struct SpriteSheet *sheets);
|
||||
void LoadTilesForSpriteSheet(const struct SpriteSheet *sheet);
|
||||
void LoadTilesForSpriteSheets(struct SpriteSheet *sheets);
|
||||
void FreeSpriteTilesByTag(u16 tag);
|
||||
void FreeSpriteTileRanges(void);
|
||||
u16 GetSpriteTileStartByTag(u16 tag);
|
||||
u16 GetSpriteTileTagByTileStart(u16 start);
|
||||
void RequestSpriteSheetCopy(const struct SpriteSheet *sheet);
|
||||
u16 LoadSpriteSheetDeferred(const struct SpriteSheet *sheet);
|
||||
void FreeAllSpritePalettes(void);
|
||||
u8 LoadSpritePalette(const struct SpritePalette *palette);
|
||||
void LoadSpritePalettes(const struct SpritePalette *palettes);
|
||||
u8 AllocSpritePalette(u16 tag);
|
||||
u8 IndexOfSpritePaletteTag(u16 tag);
|
||||
u16 GetSpritePaletteTagByPaletteNum(u8 paletteNum);
|
||||
void FreeSpritePaletteByTag(u16 tag);
|
||||
void SetSubspriteTables(struct Sprite *sprite, const struct SubspriteTable *subspriteTables);
|
||||
bool8 AddSpriteToOamBuffer(struct Sprite *object, u8 *oamIndex);
|
||||
bool8 AddSubspritesToOamBuffer(struct Sprite *sprite, struct OamData *destOam, u8 *oamIndex);
|
||||
void CopyToSprites(u8 *src);
|
||||
void CopyFromSprites(u8 *dest);
|
||||
u8 SpriteTileAllocBitmapOp(u16 bit, u8 op);
|
||||
void ClearSpriteCopyRequests(void);
|
||||
void ResetAffineAnimData(void);
|
||||
|
||||
#endif //GUARD_SPRITE_H
|
781
gflib/string_util.c
Normal file
781
gflib/string_util.c
Normal file
@ -0,0 +1,781 @@
|
||||
#include "global.h"
|
||||
#include "string_util.h"
|
||||
#include "text.h"
|
||||
#include "strings.h"
|
||||
|
||||
EWRAM_DATA u8 gStringVar1[0x100] = {0};
|
||||
EWRAM_DATA u8 gStringVar2[0x100] = {0};
|
||||
EWRAM_DATA u8 gStringVar3[0x100] = {0};
|
||||
EWRAM_DATA u8 gStringVar4[0x3E8] = {0};
|
||||
EWRAM_DATA static u8 sUnknownStringVar[16] = {0};
|
||||
|
||||
static const u8 sDigits[] = __("0123456789ABCDEF");
|
||||
|
||||
static const s32 sPowersOfTen[] =
|
||||
{
|
||||
1,
|
||||
10,
|
||||
100,
|
||||
1000,
|
||||
10000,
|
||||
100000,
|
||||
1000000,
|
||||
10000000,
|
||||
100000000,
|
||||
1000000000,
|
||||
};
|
||||
|
||||
u8 *StringCopy_Nickname(u8 *dest, const u8 *src)
|
||||
{
|
||||
u8 i;
|
||||
u32 limit = POKEMON_NAME_LENGTH;
|
||||
|
||||
for (i = 0; i < limit; i++)
|
||||
{
|
||||
dest[i] = src[i];
|
||||
|
||||
if (dest[i] == EOS)
|
||||
return &dest[i];
|
||||
}
|
||||
|
||||
dest[i] = EOS;
|
||||
return &dest[i];
|
||||
}
|
||||
|
||||
u8 *StringGet_Nickname(u8 *str)
|
||||
{
|
||||
u8 i;
|
||||
u32 limit = POKEMON_NAME_LENGTH;
|
||||
|
||||
for (i = 0; i < limit; i++)
|
||||
if (str[i] == EOS)
|
||||
return &str[i];
|
||||
|
||||
str[i] = EOS;
|
||||
return &str[i];
|
||||
}
|
||||
|
||||
u8 *StringCopy_PlayerName(u8 *dest, const u8 *src)
|
||||
{
|
||||
s32 i;
|
||||
s32 limit = PLAYER_NAME_LENGTH;
|
||||
|
||||
for (i = 0; i < limit; i++)
|
||||
{
|
||||
dest[i] = src[i];
|
||||
|
||||
if (dest[i] == EOS)
|
||||
return &dest[i];
|
||||
}
|
||||
|
||||
dest[i] = EOS;
|
||||
return &dest[i];
|
||||
}
|
||||
|
||||
u8 *StringCopy(u8 *dest, const u8 *src)
|
||||
{
|
||||
while (*src != EOS)
|
||||
{
|
||||
*dest = *src;
|
||||
dest++;
|
||||
src++;
|
||||
}
|
||||
|
||||
*dest = EOS;
|
||||
return dest;
|
||||
}
|
||||
|
||||
u8 *StringAppend(u8 *dest, const u8 *src)
|
||||
{
|
||||
while (*dest != EOS)
|
||||
dest++;
|
||||
|
||||
return StringCopy(dest, src);
|
||||
}
|
||||
|
||||
u8 *StringCopyN(u8 *dest, const u8 *src, u8 n)
|
||||
{
|
||||
u16 i;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
dest[i] = src[i];
|
||||
|
||||
return &dest[n];
|
||||
}
|
||||
|
||||
u8 *StringAppendN(u8 *dest, const u8 *src, u8 n)
|
||||
{
|
||||
while (*dest != EOS)
|
||||
dest++;
|
||||
|
||||
return StringCopyN(dest, src, n);
|
||||
}
|
||||
|
||||
u16 StringLength(const u8 *str)
|
||||
{
|
||||
u16 length = 0;
|
||||
|
||||
while (str[length] != EOS)
|
||||
length++;
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
s32 StringCompare(const u8 *str1, const u8 *str2)
|
||||
{
|
||||
while (*str1 == *str2)
|
||||
{
|
||||
if (*str1 == EOS)
|
||||
return 0;
|
||||
str1++;
|
||||
str2++;
|
||||
}
|
||||
|
||||
return *str1 - *str2;
|
||||
}
|
||||
|
||||
s32 StringCompareN(const u8 *str1, const u8 *str2, u32 n)
|
||||
{
|
||||
while (*str1 == *str2)
|
||||
{
|
||||
if (*str1 == EOS)
|
||||
return 0;
|
||||
str1++;
|
||||
str2++;
|
||||
if (--n == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return *str1 - *str2;
|
||||
}
|
||||
|
||||
bool8 IsStringLengthAtLeast(const u8 *str, s32 n)
|
||||
{
|
||||
u8 i;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
if (str[i] && str[i] != EOS)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
u8 *ConvertIntToDecimalStringN(u8 *dest, s32 value, enum StringConvertMode mode, u8 n)
|
||||
{
|
||||
enum { WAITING_FOR_NONZERO_DIGIT, WRITING_DIGITS, WRITING_SPACES } state;
|
||||
s32 powerOfTen;
|
||||
s32 largestPowerOfTen = sPowersOfTen[n - 1];
|
||||
|
||||
state = WAITING_FOR_NONZERO_DIGIT;
|
||||
|
||||
if (mode == STR_CONV_MODE_RIGHT_ALIGN)
|
||||
state = WRITING_SPACES;
|
||||
|
||||
if (mode == STR_CONV_MODE_LEADING_ZEROS)
|
||||
state = WRITING_DIGITS;
|
||||
|
||||
for (powerOfTen = largestPowerOfTen; powerOfTen > 0; powerOfTen /= 10)
|
||||
{
|
||||
u8 c;
|
||||
u16 digit = value / powerOfTen;
|
||||
s32 temp = value - (powerOfTen * digit);
|
||||
|
||||
if (state == WRITING_DIGITS)
|
||||
{
|
||||
u8 *out = dest++;
|
||||
|
||||
if (digit <= 9)
|
||||
c = sDigits[digit];
|
||||
else
|
||||
c = CHAR_QUESTION_MARK;
|
||||
|
||||
*out = c;
|
||||
}
|
||||
else if (digit != 0 || powerOfTen == 1)
|
||||
{
|
||||
u8 *out;
|
||||
state = WRITING_DIGITS;
|
||||
out = dest++;
|
||||
|
||||
if (digit <= 9)
|
||||
c = sDigits[digit];
|
||||
else
|
||||
c = CHAR_QUESTION_MARK;
|
||||
|
||||
*out = c;
|
||||
}
|
||||
else if (state == WRITING_SPACES)
|
||||
{
|
||||
*dest++ = CHAR_SPACER;
|
||||
}
|
||||
|
||||
value = temp;
|
||||
}
|
||||
|
||||
*dest = EOS;
|
||||
return dest;
|
||||
}
|
||||
|
||||
u8 *ConvertUIntToDecimalStringN(u8 *dest, u32 value, enum StringConvertMode mode, u8 n)
|
||||
{
|
||||
enum { WAITING_FOR_NONZERO_DIGIT, WRITING_DIGITS, WRITING_SPACES } state;
|
||||
s32 powerOfTen;
|
||||
s32 largestPowerOfTen = sPowersOfTen[n - 1];
|
||||
|
||||
state = WAITING_FOR_NONZERO_DIGIT;
|
||||
|
||||
if (mode == STR_CONV_MODE_RIGHT_ALIGN)
|
||||
state = WRITING_SPACES;
|
||||
|
||||
if (mode == STR_CONV_MODE_LEADING_ZEROS)
|
||||
state = WRITING_DIGITS;
|
||||
|
||||
for (powerOfTen = largestPowerOfTen; powerOfTen > 0; powerOfTen /= 10)
|
||||
{
|
||||
u8 c;
|
||||
u16 digit = value / powerOfTen;
|
||||
u32 temp = value - (powerOfTen * digit);
|
||||
|
||||
if (state == WRITING_DIGITS)
|
||||
{
|
||||
u8 *out = dest++;
|
||||
|
||||
if (digit <= 9)
|
||||
c = sDigits[digit];
|
||||
else
|
||||
c = CHAR_QUESTION_MARK;
|
||||
|
||||
*out = c;
|
||||
}
|
||||
else if (digit != 0 || powerOfTen == 1)
|
||||
{
|
||||
u8 *out;
|
||||
state = WRITING_DIGITS;
|
||||
out = dest++;
|
||||
|
||||
if (digit <= 9)
|
||||
c = sDigits[digit];
|
||||
else
|
||||
c = CHAR_QUESTION_MARK;
|
||||
|
||||
*out = c;
|
||||
}
|
||||
else if (state == WRITING_SPACES)
|
||||
{
|
||||
*dest++ = CHAR_SPACER;
|
||||
}
|
||||
|
||||
value = temp;
|
||||
}
|
||||
|
||||
*dest = EOS;
|
||||
return dest;
|
||||
}
|
||||
|
||||
u8 *ConvertIntToHexStringN(u8 *dest, s32 value, enum StringConvertMode mode, u8 n)
|
||||
{
|
||||
enum { WAITING_FOR_NONZERO_DIGIT, WRITING_DIGITS, WRITING_SPACES } state;
|
||||
u8 i;
|
||||
s32 powerOfSixteen;
|
||||
s32 largestPowerOfSixteen = 1;
|
||||
|
||||
for (i = 1; i < n; i++)
|
||||
largestPowerOfSixteen *= 16;
|
||||
|
||||
state = WAITING_FOR_NONZERO_DIGIT;
|
||||
|
||||
if (mode == STR_CONV_MODE_RIGHT_ALIGN)
|
||||
state = WRITING_SPACES;
|
||||
|
||||
if (mode == STR_CONV_MODE_LEADING_ZEROS)
|
||||
state = WRITING_DIGITS;
|
||||
|
||||
for (powerOfSixteen = largestPowerOfSixteen; powerOfSixteen > 0; powerOfSixteen /= 16)
|
||||
{
|
||||
u8 c;
|
||||
u32 digit = value / powerOfSixteen;
|
||||
s32 temp = value % powerOfSixteen;
|
||||
|
||||
if (state == WRITING_DIGITS)
|
||||
{
|
||||
char *out = dest++;
|
||||
|
||||
if (digit <= 0xF)
|
||||
c = sDigits[digit];
|
||||
else
|
||||
c = CHAR_QUESTION_MARK;
|
||||
|
||||
*out = c;
|
||||
}
|
||||
else if (digit != 0 || powerOfSixteen == 1)
|
||||
{
|
||||
char *out;
|
||||
state = WRITING_DIGITS;
|
||||
out = dest++;
|
||||
|
||||
if (digit <= 0xF)
|
||||
c = sDigits[digit];
|
||||
else
|
||||
c = CHAR_QUESTION_MARK;
|
||||
|
||||
*out = c;
|
||||
}
|
||||
else if (state == WRITING_SPACES)
|
||||
{
|
||||
*dest++ = CHAR_SPACER;
|
||||
}
|
||||
|
||||
value = temp;
|
||||
}
|
||||
|
||||
*dest = EOS;
|
||||
return dest;
|
||||
}
|
||||
|
||||
u8 *StringExpandPlaceholders(u8 *dest, const u8 *src)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
u8 c = *src++;
|
||||
u8 placeholderId;
|
||||
const u8 *expandedString;
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case PLACEHOLDER_BEGIN:
|
||||
placeholderId = *src++;
|
||||
expandedString = GetExpandedPlaceholder(placeholderId);
|
||||
dest = StringExpandPlaceholders(dest, expandedString);
|
||||
break;
|
||||
case EXT_CTRL_CODE_BEGIN:
|
||||
*dest++ = c;
|
||||
c = *src++;
|
||||
*dest++ = c;
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case EXT_CTRL_CODE_RESET_SIZE:
|
||||
case EXT_CTRL_CODE_PAUSE_UNTIL_PRESS:
|
||||
case EXT_CTRL_CODE_FILL_WINDOW:
|
||||
case EXT_CTRL_CODE_JPN:
|
||||
case EXT_CTRL_CODE_ENG:
|
||||
case EXT_CTRL_CODE_PAUSE_MUSIC:
|
||||
case EXT_CTRL_CODE_RESUME_MUSIC:
|
||||
break;
|
||||
case EXT_CTRL_CODE_COLOR_HIGHLIGHT_SHADOW:
|
||||
*dest++ = *src++;
|
||||
case EXT_CTRL_CODE_PLAY_BGM:
|
||||
*dest++ = *src++;
|
||||
default:
|
||||
*dest++ = *src++;
|
||||
}
|
||||
break;
|
||||
case EOS:
|
||||
*dest = EOS;
|
||||
return dest;
|
||||
case CHAR_PROMPT_SCROLL:
|
||||
case CHAR_PROMPT_CLEAR:
|
||||
case CHAR_NEWLINE:
|
||||
default:
|
||||
*dest++ = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u8 *StringBraille(u8 *dest, const u8 *src)
|
||||
{
|
||||
const u8 setBrailleFont[] = {
|
||||
EXT_CTRL_CODE_BEGIN,
|
||||
EXT_CTRL_CODE_FONT,
|
||||
6,
|
||||
EOS
|
||||
};
|
||||
const u8 gotoLine2[] = {
|
||||
CHAR_NEWLINE,
|
||||
EXT_CTRL_CODE_BEGIN,
|
||||
EXT_CTRL_CODE_SHIFT_DOWN,
|
||||
2,
|
||||
EOS
|
||||
};
|
||||
|
||||
dest = StringCopy(dest, setBrailleFont);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
u8 c = *src++;
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case EOS:
|
||||
*dest = c;
|
||||
return dest;
|
||||
case CHAR_NEWLINE:
|
||||
dest = StringCopy(dest, gotoLine2);
|
||||
break;
|
||||
default:
|
||||
*dest++ = c;
|
||||
*dest++ = c + NUM_BRAILLE_CHARS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const u8 *ExpandPlaceholder_UnknownStringVar(void)
|
||||
{
|
||||
return sUnknownStringVar;
|
||||
}
|
||||
|
||||
static const u8 *ExpandPlaceholder_PlayerName(void)
|
||||
{
|
||||
return gSaveBlock2Ptr->playerName;
|
||||
}
|
||||
|
||||
static const u8 *ExpandPlaceholder_StringVar1(void)
|
||||
{
|
||||
return gStringVar1;
|
||||
}
|
||||
|
||||
static const u8 *ExpandPlaceholder_StringVar2(void)
|
||||
{
|
||||
return gStringVar2;
|
||||
}
|
||||
|
||||
static const u8 *ExpandPlaceholder_StringVar3(void)
|
||||
{
|
||||
return gStringVar3;
|
||||
}
|
||||
|
||||
static const u8 *ExpandPlaceholder_KunChan(void)
|
||||
{
|
||||
if (gSaveBlock2Ptr->playerGender == MALE)
|
||||
return gText_ExpandedPlaceholder_Kun;
|
||||
else
|
||||
return gText_ExpandedPlaceholder_Chan;
|
||||
}
|
||||
|
||||
static const u8 *ExpandPlaceholder_RivalName(void)
|
||||
{
|
||||
if (gSaveBlock2Ptr->playerGender == MALE)
|
||||
return gText_ExpandedPlaceholder_May;
|
||||
else
|
||||
return gText_ExpandedPlaceholder_Brendan;
|
||||
}
|
||||
|
||||
static const u8 *ExpandPlaceholder_Version(void)
|
||||
{
|
||||
return gText_ExpandedPlaceholder_Emerald;
|
||||
}
|
||||
|
||||
static const u8 *ExpandPlaceholder_Aqua(void)
|
||||
{
|
||||
return gText_ExpandedPlaceholder_Aqua;
|
||||
}
|
||||
|
||||
static const u8 *ExpandPlaceholder_Magma(void)
|
||||
{
|
||||
return gText_ExpandedPlaceholder_Magma;
|
||||
}
|
||||
|
||||
static const u8 *ExpandPlaceholder_Archie(void)
|
||||
{
|
||||
return gText_ExpandedPlaceholder_Archie;
|
||||
}
|
||||
|
||||
static const u8 *ExpandPlaceholder_Maxie(void)
|
||||
{
|
||||
return gText_ExpandedPlaceholder_Maxie;
|
||||
}
|
||||
|
||||
static const u8 *ExpandPlaceholder_Kyogre(void)
|
||||
{
|
||||
return gText_ExpandedPlaceholder_Kyogre;
|
||||
}
|
||||
|
||||
static const u8 *ExpandPlaceholder_Groudon(void)
|
||||
{
|
||||
return gText_ExpandedPlaceholder_Groudon;
|
||||
}
|
||||
|
||||
const u8 *GetExpandedPlaceholder(u32 id)
|
||||
{
|
||||
typedef const u8 *(*ExpandPlaceholderFunc)(void);
|
||||
|
||||
static const ExpandPlaceholderFunc funcs[] =
|
||||
{
|
||||
[PLACEHOLDER_ID_UNKNOWN] = ExpandPlaceholder_UnknownStringVar,
|
||||
[PLACEHOLDER_ID_PLAYER] = ExpandPlaceholder_PlayerName,
|
||||
[PLACEHOLDER_ID_STRING_VAR_1] = ExpandPlaceholder_StringVar1,
|
||||
[PLACEHOLDER_ID_STRING_VAR_2] = ExpandPlaceholder_StringVar2,
|
||||
[PLACEHOLDER_ID_STRING_VAR_3] = ExpandPlaceholder_StringVar3,
|
||||
[PLACEHOLDER_ID_KUN] = ExpandPlaceholder_KunChan,
|
||||
[PLACEHOLDER_ID_RIVAL] = ExpandPlaceholder_RivalName,
|
||||
[PLACEHOLDER_ID_VERSION] = ExpandPlaceholder_Version,
|
||||
[PLACEHOLDER_ID_AQUA] = ExpandPlaceholder_Aqua,
|
||||
[PLACEHOLDER_ID_MAGMA] = ExpandPlaceholder_Magma,
|
||||
[PLACEHOLDER_ID_ARCHIE] = ExpandPlaceholder_Archie,
|
||||
[PLACEHOLDER_ID_MAXIE] = ExpandPlaceholder_Maxie,
|
||||
[PLACEHOLDER_ID_KYOGRE] = ExpandPlaceholder_Kyogre,
|
||||
[PLACEHOLDER_ID_GROUDON] = ExpandPlaceholder_Groudon,
|
||||
};
|
||||
|
||||
if (id >= ARRAY_COUNT(funcs))
|
||||
return gText_ExpandedPlaceholder_Empty;
|
||||
else
|
||||
return funcs[id]();
|
||||
}
|
||||
|
||||
u8 *StringFill(u8 *dest, u8 c, u16 n)
|
||||
{
|
||||
u16 i;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
*dest++ = c;
|
||||
|
||||
*dest = EOS;
|
||||
return dest;
|
||||
}
|
||||
|
||||
u8 *StringCopyPadded(u8 *dest, const u8 *src, u8 c, u16 n)
|
||||
{
|
||||
while (*src != EOS)
|
||||
{
|
||||
*dest++ = *src++;
|
||||
|
||||
if (n)
|
||||
n--;
|
||||
}
|
||||
|
||||
n--;
|
||||
|
||||
while (n != (u16)-1)
|
||||
{
|
||||
*dest++ = c;
|
||||
n--;
|
||||
}
|
||||
|
||||
*dest = EOS;
|
||||
return dest;
|
||||
}
|
||||
|
||||
u8 *StringFillWithTerminator(u8 *dest, u16 n)
|
||||
{
|
||||
return StringFill(dest, EOS, n);
|
||||
}
|
||||
|
||||
u8 *StringCopyN_Multibyte(u8 *dest, u8 *src, u32 n)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
for (i = n - 1; i != (u32)-1; i--)
|
||||
{
|
||||
if (*src == EOS)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
*dest++ = *src++;
|
||||
if (*(src - 1) == CHAR_EXTRA_SYMBOL)
|
||||
*dest++ = *src++;
|
||||
}
|
||||
}
|
||||
|
||||
*dest = EOS;
|
||||
return dest;
|
||||
}
|
||||
|
||||
u32 StringLength_Multibyte(const u8 *str)
|
||||
{
|
||||
u32 length = 0;
|
||||
|
||||
while (*str != EOS)
|
||||
{
|
||||
if (*str == CHAR_EXTRA_SYMBOL)
|
||||
str++;
|
||||
str++;
|
||||
length++;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
u8 *WriteColorChangeControlCode(u8 *dest, u32 colorType, u8 color)
|
||||
{
|
||||
*dest = EXT_CTRL_CODE_BEGIN;
|
||||
dest++;
|
||||
|
||||
switch (colorType)
|
||||
{
|
||||
case 0:
|
||||
*dest = EXT_CTRL_CODE_COLOR;
|
||||
dest++;
|
||||
break;
|
||||
case 1:
|
||||
*dest = EXT_CTRL_CODE_SHADOW;
|
||||
dest++;
|
||||
break;
|
||||
case 2:
|
||||
*dest = EXT_CTRL_CODE_HIGHLIGHT;
|
||||
dest++;
|
||||
break;
|
||||
}
|
||||
|
||||
*dest = color;
|
||||
dest++;
|
||||
*dest = EOS;
|
||||
return dest;
|
||||
}
|
||||
|
||||
bool32 IsStringJapanese(u8 *str)
|
||||
{
|
||||
while (*str != EOS)
|
||||
{
|
||||
if (*str <= JAPANESE_CHAR_END)
|
||||
if (*str != CHAR_SPACE)
|
||||
return TRUE;
|
||||
str++;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool32 IsStringNJapanese(u8 *str, s32 n)
|
||||
{
|
||||
s32 i;
|
||||
|
||||
for (i = 0; *str != EOS && i < n; i++)
|
||||
{
|
||||
if (*str <= JAPANESE_CHAR_END)
|
||||
if (*str != CHAR_SPACE)
|
||||
return TRUE;
|
||||
str++;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
u8 GetExtCtrlCodeLength(u8 code)
|
||||
{
|
||||
static const u8 lengths[] =
|
||||
{
|
||||
[0] = 1,
|
||||
[EXT_CTRL_CODE_COLOR] = 2,
|
||||
[EXT_CTRL_CODE_HIGHLIGHT] = 2,
|
||||
[EXT_CTRL_CODE_SHADOW] = 2,
|
||||
[EXT_CTRL_CODE_COLOR_HIGHLIGHT_SHADOW] = 4,
|
||||
[EXT_CTRL_CODE_PALETTE] = 2,
|
||||
[EXT_CTRL_CODE_FONT] = 2,
|
||||
[EXT_CTRL_CODE_RESET_SIZE] = 1,
|
||||
[EXT_CTRL_CODE_PAUSE] = 2,
|
||||
[EXT_CTRL_CODE_PAUSE_UNTIL_PRESS] = 1,
|
||||
[EXT_CTRL_CODE_WAIT_SE] = 1,
|
||||
[EXT_CTRL_CODE_PLAY_BGM] = 3,
|
||||
[EXT_CTRL_CODE_ESCAPE] = 2,
|
||||
[EXT_CTRL_CODE_SHIFT_TEXT] = 2,
|
||||
[EXT_CTRL_CODE_SHIFT_DOWN] = 2,
|
||||
[EXT_CTRL_CODE_FILL_WINDOW] = 1,
|
||||
[EXT_CTRL_CODE_PLAY_SE] = 3,
|
||||
[EXT_CTRL_CODE_CLEAR] = 2,
|
||||
[EXT_CTRL_CODE_SKIP] = 2,
|
||||
[EXT_CTRL_CODE_CLEAR_TO] = 2,
|
||||
[EXT_CTRL_CODE_MIN_LETTER_SPACING] = 2,
|
||||
[EXT_CTRL_CODE_JPN] = 1,
|
||||
[EXT_CTRL_CODE_ENG] = 1,
|
||||
[EXT_CTRL_CODE_PAUSE_MUSIC] = 1,
|
||||
[EXT_CTRL_CODE_RESUME_MUSIC] = 1,
|
||||
};
|
||||
|
||||
u8 length = 0;
|
||||
if (code < ARRAY_COUNT(lengths))
|
||||
length = lengths[code];
|
||||
return length;
|
||||
}
|
||||
|
||||
static const u8 *SkipExtCtrlCode(const u8 *s)
|
||||
{
|
||||
while (*s == EXT_CTRL_CODE_BEGIN)
|
||||
{
|
||||
s++;
|
||||
s += GetExtCtrlCodeLength(*s);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
s32 StringCompareWithoutExtCtrlCodes(const u8 *str1, const u8 *str2)
|
||||
{
|
||||
s32 retVal = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
str1 = SkipExtCtrlCode(str1);
|
||||
str2 = SkipExtCtrlCode(str2);
|
||||
|
||||
if (*str1 > *str2)
|
||||
break;
|
||||
|
||||
if (*str1 < *str2)
|
||||
{
|
||||
retVal = -1;
|
||||
if (*str2 == EOS)
|
||||
retVal = 1;
|
||||
}
|
||||
|
||||
if (*str1 == EOS)
|
||||
return retVal;
|
||||
|
||||
str1++;
|
||||
str2++;
|
||||
}
|
||||
|
||||
retVal = 1;
|
||||
|
||||
if (*str1 == EOS)
|
||||
retVal = -1;
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
void ConvertInternationalString(u8 *s, u8 language)
|
||||
{
|
||||
if (language == LANGUAGE_JAPANESE)
|
||||
{
|
||||
u8 i;
|
||||
|
||||
StripExtCtrlCodes(s);
|
||||
i = StringLength(s);
|
||||
s[i++] = EXT_CTRL_CODE_BEGIN;
|
||||
s[i++] = EXT_CTRL_CODE_ENG;
|
||||
s[i++] = EOS;
|
||||
|
||||
i--;
|
||||
|
||||
while (i != (u8)-1)
|
||||
{
|
||||
s[i + 2] = s[i];
|
||||
i--;
|
||||
}
|
||||
|
||||
s[0] = EXT_CTRL_CODE_BEGIN;
|
||||
s[1] = EXT_CTRL_CODE_JPN;
|
||||
}
|
||||
}
|
||||
|
||||
void StripExtCtrlCodes(u8 *str)
|
||||
{
|
||||
u16 srcIndex = 0;
|
||||
u16 destIndex = 0;
|
||||
while (str[srcIndex] != EOS)
|
||||
{
|
||||
if (str[srcIndex] == EXT_CTRL_CODE_BEGIN)
|
||||
{
|
||||
srcIndex++;
|
||||
srcIndex += GetExtCtrlCodeLength(str[srcIndex]);
|
||||
}
|
||||
else
|
||||
{
|
||||
str[destIndex++] = str[srcIndex++];
|
||||
}
|
||||
}
|
||||
str[destIndex] = EOS;
|
||||
}
|
46
gflib/string_util.h
Normal file
46
gflib/string_util.h
Normal file
@ -0,0 +1,46 @@
|
||||
#ifndef GUARD_STRING_UTIL_H
|
||||
#define GUARD_STRING_UTIL_H
|
||||
|
||||
extern u8 gStringVar1[0x100];
|
||||
extern u8 gStringVar2[0x100];
|
||||
extern u8 gStringVar3[0x100];
|
||||
extern u8 gStringVar4[0x3E8];
|
||||
|
||||
enum StringConvertMode
|
||||
{
|
||||
STR_CONV_MODE_LEFT_ALIGN,
|
||||
STR_CONV_MODE_RIGHT_ALIGN,
|
||||
STR_CONV_MODE_LEADING_ZEROS
|
||||
};
|
||||
|
||||
u8 *StringCopy_Nickname(u8 *dest, const u8 *src);
|
||||
u8 *StringGet_Nickname(u8 *str);
|
||||
u8 *StringCopy_PlayerName(u8 *dest, const u8 *src);
|
||||
u8 *StringCopy(u8 *dest, const u8 *src);
|
||||
u8 *StringAppend(u8 *dest, const u8 *src);
|
||||
u8 *StringCopyN(u8 *dest, const u8 *src, u8 n);
|
||||
u8 *StringAppendN(u8 *dest, const u8 *src, u8 n);
|
||||
u16 StringLength(const u8 *str);
|
||||
s32 StringCompare(const u8 *str1, const u8 *str2);
|
||||
s32 StringCompareN(const u8 *str1, const u8 *str2, u32 n);
|
||||
bool8 IsStringLengthAtLeast(const u8 *str, s32 n);
|
||||
u8 *ConvertIntToDecimalStringN(u8 *dest, s32 value, enum StringConvertMode mode, u8 n);
|
||||
u8 *ConvertUIntToDecimalStringN(u8 *dest, u32 value, enum StringConvertMode mode, u8 n);
|
||||
u8 *ConvertIntToHexStringN(u8 *dest, s32 value, enum StringConvertMode mode, u8 n);
|
||||
u8 *StringExpandPlaceholders(u8 *dest, const u8 *src);
|
||||
u8 *StringBraille(u8 *dest, const u8 *src);
|
||||
const u8 *GetExpandedPlaceholder(u32 id);
|
||||
u8 *StringFill(u8 *dest, u8 c, u16 n);
|
||||
u8 *StringCopyPadded(u8 *dest, const u8 *src, u8 c, u16 n);
|
||||
u8 *StringFillWithTerminator(u8 *dest, u16 n);
|
||||
u8 *StringCopyN_Multibyte(u8 *dest, u8 *src, u32 n);
|
||||
u32 StringLength_Multibyte(const u8 *str);
|
||||
u8 *WriteColorChangeControlCode(u8 *dest, u32 colorType, u8 color);
|
||||
bool32 IsStringJapanese(u8 *str);
|
||||
bool32 IsStringNJapanese(u8 *str, s32 n);
|
||||
u8 GetExtCtrlCodeLength(u8 code);
|
||||
s32 StringCompareWithoutExtCtrlCodes(const u8 *str1, const u8 *str2);
|
||||
void ConvertInternationalString(u8 *s, u8 language);
|
||||
void StripExtCtrlCodes(u8 *str);
|
||||
|
||||
#endif // GUARD_STRING_UTIL_H
|
1906
gflib/text.c
Normal file
1906
gflib/text.c
Normal file
@ -0,0 +1,1906 @@
|
||||
#include "global.h"
|
||||
#include "battle.h"
|
||||
#include "main.h"
|
||||
#include "m4a.h"
|
||||
#include "palette.h"
|
||||
#include "sound.h"
|
||||
#include "constants/songs.h"
|
||||
#include "string_util.h"
|
||||
#include "window.h"
|
||||
#include "text.h"
|
||||
#include "blit.h"
|
||||
#include "menu.h"
|
||||
#include "dynamic_placeholder_text_util.h"
|
||||
#include "fonts.h"
|
||||
|
||||
static u16 RenderText(struct TextPrinter *);
|
||||
static u32 RenderFont(struct TextPrinter *);
|
||||
static u16 FontFunc_Small(struct TextPrinter *);
|
||||
static u16 FontFunc_Normal(struct TextPrinter *);
|
||||
static u16 FontFunc_Short(struct TextPrinter *);
|
||||
static u16 FontFunc_ShortCopy1(struct TextPrinter *);
|
||||
static u16 FontFunc_ShortCopy2(struct TextPrinter *);
|
||||
static u16 FontFunc_ShortCopy3(struct TextPrinter *);
|
||||
static u16 FontFunc_Narrow(struct TextPrinter *);
|
||||
static u16 FontFunc_SmallNarrow(struct TextPrinter *);
|
||||
static void DecompressGlyph_Small(u16, bool32);
|
||||
static void DecompressGlyph_Normal(u16, bool32);
|
||||
static void DecompressGlyph_Short(u16, bool32);
|
||||
static void DecompressGlyph_Narrow(u16, bool32);
|
||||
static void DecompressGlyph_SmallNarrow(u16, bool32);
|
||||
static void DecompressGlyph_Bold(u16);
|
||||
static u32 GetGlyphWidth_Small(u16, bool32);
|
||||
static u32 GetGlyphWidth_Normal(u16, bool32);
|
||||
static u32 GetGlyphWidth_Short(u16, bool32);
|
||||
static u32 GetGlyphWidth_Narrow(u16, bool32);
|
||||
static u32 GetGlyphWidth_SmallNarrow(u16, bool32);
|
||||
|
||||
static EWRAM_DATA struct TextPrinter sTempTextPrinter = {0};
|
||||
static EWRAM_DATA struct TextPrinter sTextPrinters[NUM_TEXT_PRINTERS] = {0};
|
||||
|
||||
static u16 sFontHalfRowLookupTable[0x51];
|
||||
static u16 sLastTextBgColor;
|
||||
static u16 sLastTextFgColor;
|
||||
static u16 sLastTextShadowColor;
|
||||
|
||||
const struct FontInfo *gFonts;
|
||||
bool8 gDisableTextPrinters;
|
||||
struct TextGlyph gCurGlyph;
|
||||
TextFlags gTextFlags;
|
||||
|
||||
static const u8 sFontHalfRowOffsets[] =
|
||||
{
|
||||
0x00, 0x01, 0x02, 0x00, 0x03, 0x04, 0x05, 0x03, 0x06, 0x07, 0x08, 0x06, 0x00, 0x01, 0x02, 0x00,
|
||||
0x09, 0x0A, 0x0B, 0x09, 0x0C, 0x0D, 0x0E, 0x0C, 0x0F, 0x10, 0x11, 0x0F, 0x09, 0x0A, 0x0B, 0x09,
|
||||
0x12, 0x13, 0x14, 0x12, 0x15, 0x16, 0x17, 0x15, 0x18, 0x19, 0x1A, 0x18, 0x12, 0x13, 0x14, 0x12,
|
||||
0x00, 0x01, 0x02, 0x00, 0x03, 0x04, 0x05, 0x03, 0x06, 0x07, 0x08, 0x06, 0x00, 0x01, 0x02, 0x00,
|
||||
0x1B, 0x1C, 0x1D, 0x1B, 0x1E, 0x1F, 0x20, 0x1E, 0x21, 0x22, 0x23, 0x21, 0x1B, 0x1C, 0x1D, 0x1B,
|
||||
0x24, 0x25, 0x26, 0x24, 0x27, 0x28, 0x29, 0x27, 0x2A, 0x2B, 0x2C, 0x2A, 0x24, 0x25, 0x26, 0x24,
|
||||
0x2D, 0x2E, 0x2F, 0x2D, 0x30, 0x31, 0x32, 0x30, 0x33, 0x34, 0x35, 0x33, 0x2D, 0x2E, 0x2F, 0x2D,
|
||||
0x1B, 0x1C, 0x1D, 0x1B, 0x1E, 0x1F, 0x20, 0x1E, 0x21, 0x22, 0x23, 0x21, 0x1B, 0x1C, 0x1D, 0x1B,
|
||||
0x36, 0x37, 0x38, 0x36, 0x39, 0x3A, 0x3B, 0x39, 0x3C, 0x3D, 0x3E, 0x3C, 0x36, 0x37, 0x38, 0x36,
|
||||
0x3F, 0x40, 0x41, 0x3F, 0x42, 0x43, 0x44, 0x42, 0x45, 0x46, 0x47, 0x45, 0x3F, 0x40, 0x41, 0x3F,
|
||||
0x48, 0x49, 0x4A, 0x48, 0x4B, 0x4C, 0x4D, 0x4B, 0x4E, 0x4F, 0x50, 0x4E, 0x48, 0x49, 0x4A, 0x48,
|
||||
0x36, 0x37, 0x38, 0x36, 0x39, 0x3A, 0x3B, 0x39, 0x3C, 0x3D, 0x3E, 0x3C, 0x36, 0x37, 0x38, 0x36,
|
||||
0x00, 0x01, 0x02, 0x00, 0x03, 0x04, 0x05, 0x03, 0x06, 0x07, 0x08, 0x06, 0x00, 0x01, 0x02, 0x00,
|
||||
0x09, 0x0A, 0x0B, 0x09, 0x0C, 0x0D, 0x0E, 0x0C, 0x0F, 0x10, 0x11, 0x0F, 0x09, 0x0A, 0x0B, 0x09,
|
||||
0x12, 0x13, 0x14, 0x12, 0x15, 0x16, 0x17, 0x15, 0x18, 0x19, 0x1A, 0x18, 0x12, 0x13, 0x14, 0x12,
|
||||
0x00, 0x01, 0x02, 0x00, 0x03, 0x04, 0x05, 0x03, 0x06, 0x07, 0x08, 0x06, 0x00, 0x01, 0x02, 0x00
|
||||
};
|
||||
|
||||
static const u8 sDownArrowTiles[] = INCBIN_U8("graphics/fonts/down_arrow.4bpp");
|
||||
static const u8 sDarkDownArrowTiles[] = INCBIN_U8("graphics/fonts/down_arrow_alt.4bpp");
|
||||
static const u8 sUnusedFRLGBlankedDownArrow[] = INCBIN_U8("graphics/fonts/unused_frlg_blanked_down_arrow.4bpp");
|
||||
static const u8 sUnusedFRLGDownArrow[] = INCBIN_U8("graphics/fonts/unused_frlg_down_arrow.4bpp");
|
||||
static const u8 sDownArrowYCoords[] = { 0, 1, 2, 1 };
|
||||
static const u8 sWindowVerticalScrollSpeeds[] = {
|
||||
[OPTIONS_TEXT_SPEED_SLOW] = 1,
|
||||
[OPTIONS_TEXT_SPEED_MID] = 2,
|
||||
[OPTIONS_TEXT_SPEED_FAST] = 4,
|
||||
};
|
||||
|
||||
static const struct GlyphWidthFunc sGlyphWidthFuncs[] =
|
||||
{
|
||||
{ FONT_SMALL, GetGlyphWidth_Small },
|
||||
{ FONT_NORMAL, GetGlyphWidth_Normal },
|
||||
{ FONT_SHORT, GetGlyphWidth_Short },
|
||||
{ FONT_SHORT_COPY_1, GetGlyphWidth_Short },
|
||||
{ FONT_SHORT_COPY_2, GetGlyphWidth_Short },
|
||||
{ FONT_SHORT_COPY_3, GetGlyphWidth_Short },
|
||||
{ FONT_BRAILLE, GetGlyphWidth_Braille },
|
||||
{ FONT_NARROW, GetGlyphWidth_Narrow },
|
||||
{ FONT_SMALL_NARROW, GetGlyphWidth_SmallNarrow }
|
||||
};
|
||||
|
||||
struct
|
||||
{
|
||||
u16 tileOffset;
|
||||
u8 width;
|
||||
u8 height;
|
||||
} static const sKeypadIcons[] =
|
||||
{
|
||||
[CHAR_A_BUTTON] = { 0x00, 8, 12 },
|
||||
[CHAR_B_BUTTON] = { 0x01, 8, 12 },
|
||||
[CHAR_L_BUTTON] = { 0x02, 16, 12 },
|
||||
[CHAR_R_BUTTON] = { 0x04, 16, 12 },
|
||||
[CHAR_START_BUTTON] = { 0x06, 24, 12 },
|
||||
[CHAR_SELECT_BUTTON] = { 0x09, 24, 12 },
|
||||
[CHAR_DPAD_UP] = { 0x0C, 8, 12 },
|
||||
[CHAR_DPAD_DOWN] = { 0x0D, 8, 12 },
|
||||
[CHAR_DPAD_LEFT] = { 0x0E, 8, 12 },
|
||||
[CHAR_DPAD_RIGHT] = { 0x0F, 8, 12 },
|
||||
[CHAR_DPAD_UPDOWN] = { 0x20, 8, 12 },
|
||||
[CHAR_DPAD_LEFTRIGHT] = { 0x21, 8, 12 },
|
||||
[CHAR_DPAD_NONE] = { 0x22, 8, 12 }
|
||||
};
|
||||
|
||||
static const u8 sKeypadIconTiles[] = INCBIN_U8("graphics/fonts/keypad_icons.4bpp");
|
||||
|
||||
static const struct FontInfo sFontInfos[] =
|
||||
{
|
||||
[FONT_SMALL] = {
|
||||
.fontFunction = FontFunc_Small,
|
||||
.maxLetterWidth = 5,
|
||||
.maxLetterHeight = 12,
|
||||
.letterSpacing = 0,
|
||||
.lineSpacing = 0,
|
||||
.fgColor = 2,
|
||||
.bgColor = 1,
|
||||
.shadowColor = 3,
|
||||
},
|
||||
[FONT_NORMAL] = {
|
||||
.fontFunction = FontFunc_Normal,
|
||||
.maxLetterWidth = 6,
|
||||
.maxLetterHeight = 16,
|
||||
.letterSpacing = 0,
|
||||
.lineSpacing = 0,
|
||||
.fgColor = 2,
|
||||
.bgColor = 1,
|
||||
.shadowColor = 3,
|
||||
},
|
||||
[FONT_SHORT] = {
|
||||
.fontFunction = FontFunc_Short,
|
||||
.maxLetterWidth = 6,
|
||||
.maxLetterHeight = 14,
|
||||
.letterSpacing = 0,
|
||||
.lineSpacing = 0,
|
||||
.fgColor = 2,
|
||||
.bgColor = 1,
|
||||
.shadowColor = 3,
|
||||
},
|
||||
[FONT_SHORT_COPY_1] = {
|
||||
.fontFunction = FontFunc_ShortCopy1,
|
||||
.maxLetterWidth = 6,
|
||||
.maxLetterHeight = 14,
|
||||
.letterSpacing = 0,
|
||||
.lineSpacing = 0,
|
||||
.fgColor = 2,
|
||||
.bgColor = 1,
|
||||
.shadowColor = 3,
|
||||
},
|
||||
[FONT_SHORT_COPY_2] = {
|
||||
.fontFunction = FontFunc_ShortCopy2,
|
||||
.maxLetterWidth = 6,
|
||||
.maxLetterHeight = 14,
|
||||
.letterSpacing = 0,
|
||||
.lineSpacing = 0,
|
||||
.fgColor = 2,
|
||||
.bgColor = 1,
|
||||
.shadowColor = 3,
|
||||
},
|
||||
[FONT_SHORT_COPY_3] = {
|
||||
.fontFunction = FontFunc_ShortCopy3,
|
||||
.maxLetterWidth = 6,
|
||||
.maxLetterHeight = 14,
|
||||
.letterSpacing = 0,
|
||||
.lineSpacing = 0,
|
||||
.fgColor = 2,
|
||||
.bgColor = 1,
|
||||
.shadowColor = 3,
|
||||
},
|
||||
[FONT_BRAILLE] = {
|
||||
.fontFunction = FontFunc_Braille,
|
||||
.maxLetterWidth = 8,
|
||||
.maxLetterHeight = 16,
|
||||
.letterSpacing = 0,
|
||||
.lineSpacing = 8,
|
||||
.fgColor = 2,
|
||||
.bgColor = 1,
|
||||
.shadowColor = 3,
|
||||
},
|
||||
[FONT_NARROW] = {
|
||||
.fontFunction = FontFunc_Narrow,
|
||||
.maxLetterWidth = 5,
|
||||
.maxLetterHeight = 16,
|
||||
.letterSpacing = 0,
|
||||
.lineSpacing = 0,
|
||||
.fgColor = 2,
|
||||
.bgColor = 1,
|
||||
.shadowColor = 3,
|
||||
},
|
||||
[FONT_SMALL_NARROW] = {
|
||||
.fontFunction = FontFunc_SmallNarrow,
|
||||
.maxLetterWidth = 5,
|
||||
.maxLetterHeight = 8,
|
||||
.letterSpacing = 0,
|
||||
.lineSpacing = 0,
|
||||
.fgColor = 2,
|
||||
.bgColor = 1,
|
||||
.shadowColor = 3,
|
||||
},
|
||||
[FONT_BOLD] = {
|
||||
.fontFunction = NULL,
|
||||
.maxLetterWidth = 8,
|
||||
.maxLetterHeight = 8,
|
||||
.letterSpacing = 0,
|
||||
.lineSpacing = 0,
|
||||
.fgColor = 1,
|
||||
.bgColor = 2,
|
||||
.shadowColor = 15,
|
||||
}
|
||||
};
|
||||
|
||||
static const u8 sMenuCursorDimensions[][2] =
|
||||
{
|
||||
[FONT_SMALL] = { 8, 12 },
|
||||
[FONT_NORMAL] = { 8, 15 },
|
||||
[FONT_SHORT] = { 8, 14 },
|
||||
[FONT_SHORT_COPY_1] = { 8, 14 },
|
||||
[FONT_SHORT_COPY_2] = { 8, 14 },
|
||||
[FONT_SHORT_COPY_3] = { 8, 14 },
|
||||
[FONT_BRAILLE] = { 8, 16 },
|
||||
[FONT_NARROW] = { 8, 15 },
|
||||
[FONT_SMALL_NARROW] = { 8, 8 },
|
||||
[FONT_BOLD] = {}
|
||||
};
|
||||
|
||||
static const u16 sFontBoldJapaneseGlyphs[] = INCBIN_U16("graphics/fonts/bold.hwjpnfont");
|
||||
|
||||
static void SetFontsPointer(const struct FontInfo *fonts)
|
||||
{
|
||||
gFonts = fonts;
|
||||
}
|
||||
|
||||
void DeactivateAllTextPrinters(void)
|
||||
{
|
||||
int printer;
|
||||
for (printer = 0; printer < NUM_TEXT_PRINTERS; ++printer)
|
||||
sTextPrinters[printer].active = FALSE;
|
||||
}
|
||||
|
||||
u16 AddTextPrinterParameterized(u8 windowId, u8 fontId, const u8 *str, u8 x, u8 y, u8 speed, void (*callback)(struct TextPrinterTemplate *, u16))
|
||||
{
|
||||
struct TextPrinterTemplate printerTemplate;
|
||||
|
||||
printerTemplate.currentChar = str;
|
||||
printerTemplate.windowId = windowId;
|
||||
printerTemplate.fontId = fontId;
|
||||
printerTemplate.x = x;
|
||||
printerTemplate.y = y;
|
||||
printerTemplate.currentX = x;
|
||||
printerTemplate.currentY = y;
|
||||
printerTemplate.letterSpacing = gFonts[fontId].letterSpacing;
|
||||
printerTemplate.lineSpacing = gFonts[fontId].lineSpacing;
|
||||
printerTemplate.unk = gFonts[fontId].unk;
|
||||
printerTemplate.fgColor = gFonts[fontId].fgColor;
|
||||
printerTemplate.bgColor = gFonts[fontId].bgColor;
|
||||
printerTemplate.shadowColor = gFonts[fontId].shadowColor;
|
||||
return AddTextPrinter(&printerTemplate, speed, callback);
|
||||
}
|
||||
|
||||
bool16 AddTextPrinter(struct TextPrinterTemplate *printerTemplate, u8 speed, void (*callback)(struct TextPrinterTemplate *, u16))
|
||||
{
|
||||
int i;
|
||||
u16 j;
|
||||
|
||||
if (!gFonts)
|
||||
return FALSE;
|
||||
|
||||
sTempTextPrinter.active = TRUE;
|
||||
sTempTextPrinter.state = RENDER_STATE_HANDLE_CHAR;
|
||||
sTempTextPrinter.textSpeed = speed;
|
||||
sTempTextPrinter.delayCounter = 0;
|
||||
sTempTextPrinter.scrollDistance = 0;
|
||||
|
||||
for (i = 0; i < (int)ARRAY_COUNT(sTempTextPrinter.subStructFields); i++)
|
||||
sTempTextPrinter.subStructFields[i] = 0;
|
||||
|
||||
sTempTextPrinter.printerTemplate = *printerTemplate;
|
||||
sTempTextPrinter.callback = callback;
|
||||
sTempTextPrinter.minLetterSpacing = 0;
|
||||
sTempTextPrinter.japanese = 0;
|
||||
|
||||
GenerateFontHalfRowLookupTable(printerTemplate->fgColor, printerTemplate->bgColor, printerTemplate->shadowColor);
|
||||
if (speed != TEXT_SKIP_DRAW && speed != 0)
|
||||
{
|
||||
--sTempTextPrinter.textSpeed;
|
||||
sTextPrinters[printerTemplate->windowId] = sTempTextPrinter;
|
||||
}
|
||||
else
|
||||
{
|
||||
sTempTextPrinter.textSpeed = 0;
|
||||
|
||||
// Render all text (up to limit) at once
|
||||
for (j = 0; j < 0x400; ++j)
|
||||
{
|
||||
if (RenderFont(&sTempTextPrinter) == RENDER_FINISH)
|
||||
break;
|
||||
}
|
||||
|
||||
// All the text is rendered to the window but don't draw it yet.
|
||||
if (speed != TEXT_SKIP_DRAW)
|
||||
CopyWindowToVram(sTempTextPrinter.printerTemplate.windowId, COPYWIN_GFX);
|
||||
sTextPrinters[printerTemplate->windowId].active = FALSE;
|
||||
}
|
||||
gDisableTextPrinters = FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void RunTextPrinters(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!gDisableTextPrinters)
|
||||
{
|
||||
for (i = 0; i < NUM_TEXT_PRINTERS; ++i)
|
||||
{
|
||||
if (sTextPrinters[i].active)
|
||||
{
|
||||
u16 temp = RenderFont(&sTextPrinters[i]);
|
||||
switch (temp)
|
||||
{
|
||||
case RENDER_PRINT:
|
||||
CopyWindowToVram(sTextPrinters[i].printerTemplate.windowId, COPYWIN_GFX);
|
||||
case RENDER_UPDATE:
|
||||
if (sTextPrinters[i].callback != 0)
|
||||
sTextPrinters[i].callback(&sTextPrinters[i].printerTemplate, temp);
|
||||
break;
|
||||
case RENDER_FINISH:
|
||||
sTextPrinters[i].active = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool16 IsTextPrinterActive(u8 id)
|
||||
{
|
||||
return sTextPrinters[id].active;
|
||||
}
|
||||
|
||||
static u32 RenderFont(struct TextPrinter *textPrinter)
|
||||
{
|
||||
u32 ret;
|
||||
while (TRUE)
|
||||
{
|
||||
ret = gFonts[textPrinter->printerTemplate.fontId].fontFunction(textPrinter);
|
||||
if (ret != RENDER_REPEAT)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateFontHalfRowLookupTable(u8 fgColor, u8 bgColor, u8 shadowColor)
|
||||
{
|
||||
u32 fg12, bg12, shadow12;
|
||||
u32 temp;
|
||||
|
||||
u16 *current = sFontHalfRowLookupTable;
|
||||
|
||||
sLastTextBgColor = bgColor;
|
||||
sLastTextFgColor = fgColor;
|
||||
sLastTextShadowColor = shadowColor;
|
||||
|
||||
bg12 = bgColor << 12;
|
||||
fg12 = fgColor << 12;
|
||||
shadow12 = shadowColor << 12;
|
||||
|
||||
temp = (bgColor << 8) | (bgColor << 4) | bgColor;
|
||||
*(current++) = (bg12) | temp;
|
||||
*(current++) = (fg12) | temp;
|
||||
*(current++) = (shadow12) | temp;
|
||||
|
||||
temp = (fgColor << 8) | (bgColor << 4) | bgColor;
|
||||
*(current++) = (bg12) | temp;
|
||||
*(current++) = (fg12) | temp;
|
||||
*(current++) = (shadow12) | temp;
|
||||
|
||||
temp = (shadowColor << 8) | (bgColor << 4) | bgColor;
|
||||
*(current++) = (bg12) | temp;
|
||||
*(current++) = (fg12) | temp;
|
||||
*(current++) = (shadow12) | temp;
|
||||
|
||||
temp = (bgColor << 8) | (fgColor << 4) | bgColor;
|
||||
*(current++) = (bg12) | temp;
|
||||
*(current++) = (fg12) | temp;
|
||||
*(current++) = (shadow12) | temp;
|
||||
|
||||
temp = (fgColor << 8) | (fgColor << 4) | bgColor;
|
||||
*(current++) = (bg12) | temp;
|
||||
*(current++) = (fg12) | temp;
|
||||
*(current++) = (shadow12) | temp;
|
||||
|
||||
temp = (shadowColor << 8) | (fgColor << 4) | bgColor;
|
||||
*(current++) = (bg12) | temp;
|
||||
*(current++) = (fg12) | temp;
|
||||
*(current++) = (shadow12) | temp;
|
||||
|
||||
temp = (bgColor << 8) | (shadowColor << 4) | bgColor;
|
||||
*(current++) = (bg12) | temp;
|
||||
*(current++) = (fg12) | temp;
|
||||
*(current++) = (shadow12) | temp;
|
||||
|
||||
temp = (fgColor << 8) | (shadowColor << 4) | bgColor;
|
||||
*(current++) = (bg12) | temp;
|
||||
*(current++) = (fg12) | temp;
|
||||
*(current++) = (shadow12) | temp;
|
||||
|
||||
temp = (shadowColor << 8) | (shadowColor << 4) | bgColor;
|
||||
*(current++) = (bg12) | temp;
|
||||
*(current++) = (fg12) | temp;
|
||||
*(current++) = (shadow12) | temp;
|
||||
|
||||
temp = (bgColor << 8) | (bgColor << 4) | fgColor;
|
||||
*(current++) = (bg12) | temp;
|
||||
*(current++) = (fg12) | temp;
|
||||
*(current++) = (shadow12) | temp;
|
||||
|
||||
temp = (fgColor << 8) | (bgColor << 4) | fgColor;
|
||||
*(current++) = (bg12) | temp;
|
||||
*(current++) = (fg12) | temp;
|
||||
*(current++) = (shadow12) | temp;
|
||||
|
||||
temp = (shadowColor << 8) | (bgColor << 4) | fgColor;
|
||||
*(current++) = (bg12) | temp;
|
||||
*(current++) = (fg12) | temp;
|
||||
*(current++) = (shadow12) | temp;
|
||||
|
||||
temp = (bgColor << 8) | (fgColor << 4) | fgColor;
|
||||
*(current++) = (bg12) | temp;
|
||||
*(current++) = (fg12) | temp;
|
||||
*(current++) = (shadow12) | temp;
|
||||
|
||||
temp = (fgColor << 8) | (fgColor << 4) | fgColor;
|
||||
*(current++) = (bg12) | temp;
|
||||
*(current++) = (fg12) | temp;
|
||||
*(current++) = (shadow12) | temp;
|
||||
|
||||
temp = (shadowColor << 8) | (fgColor << 4) | fgColor;
|
||||
*(current++) = (bg12) | temp;
|
||||
*(current++) = (fg12) | temp;
|
||||
*(current++) = (shadow12) | temp;
|
||||
|
||||
temp = (bgColor << 8) | (shadowColor << 4) | fgColor;
|
||||
*(current++) = (bg12) | temp;
|
||||
*(current++) = (fg12) | temp;
|
||||
*(current++) = (shadow12) | temp;
|
||||
|
||||
temp = (fgColor << 8) | (shadowColor << 4) | fgColor;
|
||||
*(current++) = (bg12) | temp;
|
||||
*(current++) = (fg12) | temp;
|
||||
*(current++) = (shadow12) | temp;
|
||||
|
||||
temp = (shadowColor << 8) | (shadowColor << 4) | fgColor;
|
||||
*(current++) = (bg12) | temp;
|
||||
*(current++) = (fg12) | temp;
|
||||
*(current++) = (shadow12) | temp;
|
||||
|
||||
temp = (bgColor << 8) | (bgColor << 4) | shadowColor;
|
||||
*(current++) = (bg12) | temp;
|
||||
*(current++) = (fg12) | temp;
|
||||
*(current++) = (shadow12) | temp;
|
||||
|
||||
temp = (fgColor << 8) | (bgColor << 4) | shadowColor;
|
||||
*(current++) = (bg12) | temp;
|
||||
*(current++) = (fg12) | temp;
|
||||
*(current++) = (shadow12) | temp;
|
||||
|
||||
temp = (shadowColor << 8) | (bgColor << 4) | shadowColor;
|
||||
*(current++) = (bg12) | temp;
|
||||
*(current++) = (fg12) | temp;
|
||||
*(current++) = (shadow12) | temp;
|
||||
|
||||
temp = (bgColor << 8) | (fgColor << 4) | shadowColor;
|
||||
*(current++) = (bg12) | temp;
|
||||
*(current++) = (fg12) | temp;
|
||||
*(current++) = (shadow12) | temp;
|
||||
|
||||
temp = (fgColor << 8) | (fgColor << 4) | shadowColor;
|
||||
*(current++) = (bg12) | temp;
|
||||
*(current++) = (fg12) | temp;
|
||||
*(current++) = (shadow12) | temp;
|
||||
|
||||
temp = (shadowColor << 8) | (fgColor << 4) | shadowColor;
|
||||
*(current++) = (bg12) | temp;
|
||||
*(current++) = (fg12) | temp;
|
||||
*(current++) = (shadow12) | temp;
|
||||
|
||||
temp = (bgColor << 8) | (shadowColor << 4) | shadowColor;
|
||||
*(current++) = (bg12) | temp;
|
||||
*(current++) = (fg12) | temp;
|
||||
*(current++) = (shadow12) | temp;
|
||||
|
||||
temp = (fgColor << 8) | (shadowColor << 4) | shadowColor;
|
||||
*(current++) = (bg12) | temp;
|
||||
*(current++) = (fg12) | temp;
|
||||
*(current++) = (shadow12) | temp;
|
||||
|
||||
temp = (shadowColor << 8) | (shadowColor << 4) | shadowColor;
|
||||
*(current++) = (bg12) | temp;
|
||||
*(current++) = (fg12) | temp;
|
||||
*(current++) = (shadow12) | temp;
|
||||
}
|
||||
|
||||
void SaveTextColors(u8 *fgColor, u8 *bgColor, u8 *shadowColor)
|
||||
{
|
||||
*bgColor = sLastTextBgColor;
|
||||
*fgColor = sLastTextFgColor;
|
||||
*shadowColor = sLastTextShadowColor;
|
||||
}
|
||||
|
||||
void RestoreTextColors(u8 *fgColor, u8 *bgColor, u8 *shadowColor)
|
||||
{
|
||||
GenerateFontHalfRowLookupTable(*fgColor, *bgColor, *shadowColor);
|
||||
}
|
||||
|
||||
void DecompressGlyphTile(const void *src_, void *dest_)
|
||||
{
|
||||
u32 temp;
|
||||
const u16 *src = src_;
|
||||
u32 *dest = dest_;
|
||||
|
||||
temp = *(src++);
|
||||
*(dest)++ = ((sFontHalfRowLookupTable[sFontHalfRowOffsets[temp & 0xFF]]) << 16) | (sFontHalfRowLookupTable[sFontHalfRowOffsets[temp >> 8]]);
|
||||
|
||||
temp = *(src++);
|
||||
*(dest++) = ((sFontHalfRowLookupTable[sFontHalfRowOffsets[temp & 0xFF]]) << 16) | (sFontHalfRowLookupTable[sFontHalfRowOffsets[temp >> 8]]);
|
||||
|
||||
temp = *(src++);
|
||||
*(dest++) = ((sFontHalfRowLookupTable[sFontHalfRowOffsets[temp & 0xFF]]) << 16) | (sFontHalfRowLookupTable[sFontHalfRowOffsets[temp >> 8]]);
|
||||
|
||||
temp = *(src++);
|
||||
*(dest++) = ((sFontHalfRowLookupTable[sFontHalfRowOffsets[temp & 0xFF]]) << 16) | (sFontHalfRowLookupTable[sFontHalfRowOffsets[temp >> 8]]);
|
||||
|
||||
temp = *(src++);
|
||||
*(dest++) = ((sFontHalfRowLookupTable[sFontHalfRowOffsets[temp & 0xFF]]) << 16) | (sFontHalfRowLookupTable[sFontHalfRowOffsets[temp >> 8]]);
|
||||
|
||||
temp = *(src++);
|
||||
*(dest++) = ((sFontHalfRowLookupTable[sFontHalfRowOffsets[temp & 0xFF]]) << 16) | (sFontHalfRowLookupTable[sFontHalfRowOffsets[temp >> 8]]);
|
||||
|
||||
temp = *(src++);
|
||||
*(dest++) = ((sFontHalfRowLookupTable[sFontHalfRowOffsets[temp & 0xFF]]) << 16) | (sFontHalfRowLookupTable[sFontHalfRowOffsets[temp >> 8]]);
|
||||
|
||||
temp = *(src++);
|
||||
*(dest++) = ((sFontHalfRowLookupTable[sFontHalfRowOffsets[temp & 0xFF]]) << 16) | (sFontHalfRowLookupTable[sFontHalfRowOffsets[temp >> 8]]);
|
||||
}
|
||||
|
||||
// Unused
|
||||
static u8 GetLastTextColor(u8 colorType)
|
||||
{
|
||||
switch (colorType)
|
||||
{
|
||||
case 0:
|
||||
return sLastTextFgColor;
|
||||
case 2:
|
||||
return sLastTextBgColor;
|
||||
case 1:
|
||||
return sLastTextShadowColor;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline static void GLYPH_COPY(u8 *windowTiles, u32 widthOffset, u32 j, u32 i, u32 *glyphPixels, s32 width, s32 height)
|
||||
{
|
||||
u32 xAdd, yAdd, pixelData, bits, toOrr, dummyX;
|
||||
u8 *dst;
|
||||
|
||||
xAdd = j + width;
|
||||
yAdd = i + height;
|
||||
dummyX = j;
|
||||
for (; i < yAdd; i++)
|
||||
{
|
||||
pixelData = *glyphPixels++;
|
||||
for (j = dummyX; j < xAdd; j++)
|
||||
{
|
||||
if ((toOrr = pixelData & 0xF))
|
||||
{
|
||||
dst = windowTiles + ((j / 8) * 32) + ((j % 8) / 2) + ((i / 8) * widthOffset) + ((i % 8) * 4);
|
||||
bits = ((j & 1) * 4);
|
||||
*dst = (toOrr << bits) | (*dst & (0xF0 >> bits));
|
||||
}
|
||||
pixelData >>= 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CopyGlyphToWindow(struct TextPrinter *textPrinter)
|
||||
{
|
||||
struct Window *window;
|
||||
struct WindowTemplate *template;
|
||||
u32 *glyphPixels;
|
||||
u32 currX, currY, widthOffset;
|
||||
s32 glyphWidth, glyphHeight;
|
||||
u8 *windowTiles;
|
||||
|
||||
window = &gWindows[textPrinter->printerTemplate.windowId];
|
||||
template = &window->window;
|
||||
|
||||
if ((glyphWidth = (template->width * 8) - textPrinter->printerTemplate.currentX) > gCurGlyph.width)
|
||||
glyphWidth = gCurGlyph.width;
|
||||
|
||||
if ((glyphHeight = (template->height * 8) - textPrinter->printerTemplate.currentY) > gCurGlyph.height)
|
||||
glyphHeight = gCurGlyph.height;
|
||||
|
||||
currX = textPrinter->printerTemplate.currentX;
|
||||
currY = textPrinter->printerTemplate.currentY;
|
||||
glyphPixels = gCurGlyph.gfxBufferTop;
|
||||
windowTiles = window->tileData;
|
||||
widthOffset = template->width * 32;
|
||||
|
||||
if (glyphWidth < 9)
|
||||
{
|
||||
if (glyphHeight < 9)
|
||||
{
|
||||
GLYPH_COPY(windowTiles, widthOffset, currX, currY, glyphPixels, glyphWidth, glyphHeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
GLYPH_COPY(windowTiles, widthOffset, currX, currY, glyphPixels, glyphWidth, 8);
|
||||
GLYPH_COPY(windowTiles, widthOffset, currX, currY + 8, glyphPixels + 16, glyphWidth, glyphHeight - 8);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (glyphHeight < 9)
|
||||
{
|
||||
GLYPH_COPY(windowTiles, widthOffset, currX, currY, glyphPixels, 8, glyphHeight);
|
||||
GLYPH_COPY(windowTiles, widthOffset, currX + 8, currY, glyphPixels + 8, glyphWidth - 8, glyphHeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
GLYPH_COPY(windowTiles, widthOffset, currX, currY, glyphPixels, 8, 8);
|
||||
GLYPH_COPY(windowTiles, widthOffset, currX + 8, currY, glyphPixels + 8, glyphWidth - 8, 8);
|
||||
GLYPH_COPY(windowTiles, widthOffset, currX, currY + 8, glyphPixels + 16, 8, glyphHeight - 8);
|
||||
GLYPH_COPY(windowTiles, widthOffset, currX + 8, currY + 8, glyphPixels + 24, glyphWidth - 8, glyphHeight - 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClearTextSpan(struct TextPrinter *textPrinter, u32 width)
|
||||
{
|
||||
struct Window *window;
|
||||
struct Bitmap pixels_data;
|
||||
struct TextGlyph *glyph;
|
||||
u8* glyphHeight;
|
||||
|
||||
if (sLastTextBgColor != TEXT_COLOR_TRANSPARENT)
|
||||
{
|
||||
window = &gWindows[textPrinter->printerTemplate.windowId];
|
||||
pixels_data.pixels = window->tileData;
|
||||
pixels_data.width = window->window.width << 3;
|
||||
pixels_data.height = window->window.height << 3;
|
||||
|
||||
glyph = &gCurGlyph;
|
||||
glyphHeight = &glyph->height;
|
||||
|
||||
FillBitmapRect4Bit(
|
||||
&pixels_data,
|
||||
textPrinter->printerTemplate.currentX,
|
||||
textPrinter->printerTemplate.currentY,
|
||||
width,
|
||||
*glyphHeight,
|
||||
sLastTextBgColor);
|
||||
}
|
||||
}
|
||||
|
||||
static u16 FontFunc_Small(struct TextPrinter *textPrinter)
|
||||
{
|
||||
struct TextPrinterSubStruct *subStruct = (struct TextPrinterSubStruct *)(&textPrinter->subStructFields);
|
||||
|
||||
if (subStruct->hasFontIdBeenSet == FALSE)
|
||||
{
|
||||
subStruct->fontId = FONT_SMALL;
|
||||
subStruct->hasFontIdBeenSet = TRUE;
|
||||
}
|
||||
return RenderText(textPrinter);
|
||||
}
|
||||
|
||||
static u16 FontFunc_Normal(struct TextPrinter *textPrinter)
|
||||
{
|
||||
struct TextPrinterSubStruct *subStruct = (struct TextPrinterSubStruct *)(&textPrinter->subStructFields);
|
||||
|
||||
if (subStruct->hasFontIdBeenSet == FALSE)
|
||||
{
|
||||
subStruct->fontId = FONT_NORMAL;
|
||||
subStruct->hasFontIdBeenSet = TRUE;
|
||||
}
|
||||
return RenderText(textPrinter);
|
||||
}
|
||||
|
||||
static u16 FontFunc_Short(struct TextPrinter *textPrinter)
|
||||
{
|
||||
struct TextPrinterSubStruct *subStruct = (struct TextPrinterSubStruct *)(&textPrinter->subStructFields);
|
||||
|
||||
if (subStruct->hasFontIdBeenSet == FALSE)
|
||||
{
|
||||
subStruct->fontId = FONT_SHORT;
|
||||
subStruct->hasFontIdBeenSet = TRUE;
|
||||
}
|
||||
return RenderText(textPrinter);
|
||||
}
|
||||
|
||||
static u16 FontFunc_ShortCopy1(struct TextPrinter *textPrinter)
|
||||
{
|
||||
struct TextPrinterSubStruct *subStruct = (struct TextPrinterSubStruct *)(&textPrinter->subStructFields);
|
||||
|
||||
if (subStruct->hasFontIdBeenSet == FALSE)
|
||||
{
|
||||
subStruct->fontId = FONT_SHORT_COPY_1;
|
||||
subStruct->hasFontIdBeenSet = TRUE;
|
||||
}
|
||||
return RenderText(textPrinter);
|
||||
}
|
||||
|
||||
static u16 FontFunc_ShortCopy2(struct TextPrinter *textPrinter)
|
||||
{
|
||||
struct TextPrinterSubStruct *subStruct = (struct TextPrinterSubStruct *)(&textPrinter->subStructFields);
|
||||
|
||||
if (subStruct->hasFontIdBeenSet == FALSE)
|
||||
{
|
||||
subStruct->fontId = FONT_SHORT_COPY_2;
|
||||
subStruct->hasFontIdBeenSet = TRUE;
|
||||
}
|
||||
return RenderText(textPrinter);
|
||||
}
|
||||
|
||||
static u16 FontFunc_ShortCopy3(struct TextPrinter *textPrinter)
|
||||
{
|
||||
struct TextPrinterSubStruct *subStruct = (struct TextPrinterSubStruct *)(&textPrinter->subStructFields);
|
||||
|
||||
if (subStruct->hasFontIdBeenSet == FALSE)
|
||||
{
|
||||
subStruct->fontId = FONT_SHORT_COPY_3;
|
||||
subStruct->hasFontIdBeenSet = TRUE;
|
||||
}
|
||||
return RenderText(textPrinter);
|
||||
}
|
||||
|
||||
static u16 FontFunc_Narrow(struct TextPrinter *textPrinter)
|
||||
{
|
||||
struct TextPrinterSubStruct *subStruct = (struct TextPrinterSubStruct *)(&textPrinter->subStructFields);
|
||||
|
||||
if (subStruct->hasFontIdBeenSet == FALSE)
|
||||
{
|
||||
subStruct->fontId = FONT_NARROW;
|
||||
subStruct->hasFontIdBeenSet = TRUE;
|
||||
}
|
||||
return RenderText(textPrinter);
|
||||
}
|
||||
|
||||
static u16 FontFunc_SmallNarrow(struct TextPrinter *textPrinter)
|
||||
{
|
||||
struct TextPrinterSubStruct *subStruct = (struct TextPrinterSubStruct *)(&textPrinter->subStructFields);
|
||||
|
||||
if (subStruct->hasFontIdBeenSet == FALSE)
|
||||
{
|
||||
subStruct->fontId = FONT_SMALL_NARROW;
|
||||
subStruct->hasFontIdBeenSet = TRUE;
|
||||
}
|
||||
return RenderText(textPrinter);
|
||||
}
|
||||
|
||||
void TextPrinterInitDownArrowCounters(struct TextPrinter *textPrinter)
|
||||
{
|
||||
struct TextPrinterSubStruct *subStruct = (struct TextPrinterSubStruct *)(&textPrinter->subStructFields);
|
||||
|
||||
if (gTextFlags.autoScroll == 1)
|
||||
{
|
||||
subStruct->autoScrollDelay = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
subStruct->downArrowYPosIdx = 0;
|
||||
subStruct->downArrowDelay = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void TextPrinterDrawDownArrow(struct TextPrinter *textPrinter)
|
||||
{
|
||||
struct TextPrinterSubStruct *subStruct = (struct TextPrinterSubStruct *)(&textPrinter->subStructFields);
|
||||
const u8 *arrowTiles;
|
||||
|
||||
if (gTextFlags.autoScroll == 0)
|
||||
{
|
||||
if (subStruct->downArrowDelay != 0)
|
||||
{
|
||||
subStruct->downArrowDelay--;
|
||||
}
|
||||
else
|
||||
{
|
||||
FillWindowPixelRect(
|
||||
textPrinter->printerTemplate.windowId,
|
||||
textPrinter->printerTemplate.bgColor << 4 | textPrinter->printerTemplate.bgColor,
|
||||
textPrinter->printerTemplate.currentX,
|
||||
textPrinter->printerTemplate.currentY,
|
||||
8,
|
||||
16);
|
||||
|
||||
switch (gTextFlags.useAlternateDownArrow)
|
||||
{
|
||||
case FALSE:
|
||||
default:
|
||||
arrowTiles = sDownArrowTiles;
|
||||
break;
|
||||
case TRUE:
|
||||
arrowTiles = sDarkDownArrowTiles;
|
||||
break;
|
||||
}
|
||||
|
||||
BlitBitmapRectToWindow(
|
||||
textPrinter->printerTemplate.windowId,
|
||||
arrowTiles,
|
||||
0,
|
||||
sDownArrowYCoords[subStruct->downArrowYPosIdx],
|
||||
8,
|
||||
16,
|
||||
textPrinter->printerTemplate.currentX,
|
||||
textPrinter->printerTemplate.currentY,
|
||||
8,
|
||||
16);
|
||||
CopyWindowToVram(textPrinter->printerTemplate.windowId, COPYWIN_GFX);
|
||||
|
||||
subStruct->downArrowDelay = 8;
|
||||
subStruct->downArrowYPosIdx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TextPrinterClearDownArrow(struct TextPrinter *textPrinter)
|
||||
{
|
||||
FillWindowPixelRect(
|
||||
textPrinter->printerTemplate.windowId,
|
||||
textPrinter->printerTemplate.bgColor << 4 | textPrinter->printerTemplate.bgColor,
|
||||
textPrinter->printerTemplate.currentX,
|
||||
textPrinter->printerTemplate.currentY,
|
||||
8,
|
||||
16);
|
||||
CopyWindowToVram(textPrinter->printerTemplate.windowId, COPYWIN_GFX);
|
||||
}
|
||||
|
||||
bool8 TextPrinterWaitAutoMode(struct TextPrinter *textPrinter)
|
||||
{
|
||||
struct TextPrinterSubStruct *subStruct = (struct TextPrinterSubStruct *)(&textPrinter->subStructFields);
|
||||
|
||||
if (subStruct->autoScrollDelay == 49)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
subStruct->autoScrollDelay++;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
bool16 TextPrinterWaitWithDownArrow(struct TextPrinter *textPrinter)
|
||||
{
|
||||
bool8 result = FALSE;
|
||||
if (gTextFlags.autoScroll != 0)
|
||||
{
|
||||
result = TextPrinterWaitAutoMode(textPrinter);
|
||||
}
|
||||
else
|
||||
{
|
||||
TextPrinterDrawDownArrow(textPrinter);
|
||||
if (JOY_NEW(A_BUTTON | B_BUTTON))
|
||||
{
|
||||
result = TRUE;
|
||||
PlaySE(SE_SELECT);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool16 TextPrinterWait(struct TextPrinter *textPrinter)
|
||||
{
|
||||
bool16 result = FALSE;
|
||||
if (gTextFlags.autoScroll != 0)
|
||||
{
|
||||
result = TextPrinterWaitAutoMode(textPrinter);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (JOY_NEW(A_BUTTON | B_BUTTON))
|
||||
{
|
||||
result = TRUE;
|
||||
PlaySE(SE_SELECT);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void DrawDownArrow(u8 windowId, u16 x, u16 y, u8 bgColor, bool8 drawArrow, u8 *counter, u8 *yCoordIndex)
|
||||
{
|
||||
const u8 *arrowTiles;
|
||||
|
||||
if (*counter != 0)
|
||||
{
|
||||
--*counter;
|
||||
}
|
||||
else
|
||||
{
|
||||
FillWindowPixelRect(windowId, (bgColor << 4) | bgColor, x, y, 0x8, 0x10);
|
||||
if (drawArrow == 0)
|
||||
{
|
||||
switch (gTextFlags.useAlternateDownArrow)
|
||||
{
|
||||
case FALSE:
|
||||
default:
|
||||
arrowTiles = sDownArrowTiles;
|
||||
break;
|
||||
case TRUE:
|
||||
arrowTiles = sDarkDownArrowTiles;
|
||||
break;
|
||||
}
|
||||
|
||||
BlitBitmapRectToWindow(windowId, arrowTiles, 0, sDownArrowYCoords[*yCoordIndex & 3], 8, 16, x, y - 2, 8, 16);
|
||||
CopyWindowToVram(windowId, COPYWIN_GFX);
|
||||
*counter = 8;
|
||||
++*yCoordIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static u16 RenderText(struct TextPrinter *textPrinter)
|
||||
{
|
||||
struct TextPrinterSubStruct *subStruct = (struct TextPrinterSubStruct *)(&textPrinter->subStructFields);
|
||||
u16 currChar;
|
||||
s32 width;
|
||||
s32 widthHelper;
|
||||
|
||||
switch (textPrinter->state)
|
||||
{
|
||||
case RENDER_STATE_HANDLE_CHAR:
|
||||
if (JOY_HELD(A_BUTTON | B_BUTTON) && subStruct->hasPrintBeenSpedUp)
|
||||
textPrinter->delayCounter = 0;
|
||||
|
||||
if (textPrinter->delayCounter && textPrinter->textSpeed)
|
||||
{
|
||||
textPrinter->delayCounter--;
|
||||
if (gTextFlags.canABSpeedUpPrint && (JOY_NEW(A_BUTTON | B_BUTTON)))
|
||||
{
|
||||
subStruct->hasPrintBeenSpedUp = TRUE;
|
||||
textPrinter->delayCounter = 0;
|
||||
}
|
||||
return RENDER_UPDATE;
|
||||
}
|
||||
|
||||
if (!(gBattleTypeFlags & BATTLE_TYPE_RECORDED) && gTextFlags.autoScroll)
|
||||
textPrinter->delayCounter = 3;
|
||||
else
|
||||
textPrinter->delayCounter = textPrinter->textSpeed;
|
||||
|
||||
currChar = *textPrinter->printerTemplate.currentChar;
|
||||
textPrinter->printerTemplate.currentChar++;
|
||||
|
||||
switch (currChar)
|
||||
{
|
||||
case CHAR_NEWLINE:
|
||||
textPrinter->printerTemplate.currentX = textPrinter->printerTemplate.x;
|
||||
textPrinter->printerTemplate.currentY += (gFonts[textPrinter->printerTemplate.fontId].maxLetterHeight + textPrinter->printerTemplate.lineSpacing);
|
||||
return RENDER_REPEAT;
|
||||
case PLACEHOLDER_BEGIN:
|
||||
textPrinter->printerTemplate.currentChar++;
|
||||
return RENDER_REPEAT;
|
||||
case EXT_CTRL_CODE_BEGIN:
|
||||
currChar = *textPrinter->printerTemplate.currentChar;
|
||||
textPrinter->printerTemplate.currentChar++;
|
||||
switch (currChar)
|
||||
{
|
||||
case EXT_CTRL_CODE_COLOR:
|
||||
textPrinter->printerTemplate.fgColor = *textPrinter->printerTemplate.currentChar;
|
||||
textPrinter->printerTemplate.currentChar++;
|
||||
GenerateFontHalfRowLookupTable(textPrinter->printerTemplate.fgColor, textPrinter->printerTemplate.bgColor, textPrinter->printerTemplate.shadowColor);
|
||||
return RENDER_REPEAT;
|
||||
case EXT_CTRL_CODE_HIGHLIGHT:
|
||||
textPrinter->printerTemplate.bgColor = *textPrinter->printerTemplate.currentChar;
|
||||
textPrinter->printerTemplate.currentChar++;
|
||||
GenerateFontHalfRowLookupTable(textPrinter->printerTemplate.fgColor, textPrinter->printerTemplate.bgColor, textPrinter->printerTemplate.shadowColor);
|
||||
return RENDER_REPEAT;
|
||||
case EXT_CTRL_CODE_SHADOW:
|
||||
textPrinter->printerTemplate.shadowColor = *textPrinter->printerTemplate.currentChar;
|
||||
textPrinter->printerTemplate.currentChar++;
|
||||
GenerateFontHalfRowLookupTable(textPrinter->printerTemplate.fgColor, textPrinter->printerTemplate.bgColor, textPrinter->printerTemplate.shadowColor);
|
||||
return RENDER_REPEAT;
|
||||
case EXT_CTRL_CODE_COLOR_HIGHLIGHT_SHADOW:
|
||||
textPrinter->printerTemplate.fgColor = *textPrinter->printerTemplate.currentChar;
|
||||
textPrinter->printerTemplate.currentChar++;
|
||||
textPrinter->printerTemplate.bgColor = *textPrinter->printerTemplate.currentChar;
|
||||
textPrinter->printerTemplate.currentChar++;
|
||||
textPrinter->printerTemplate.shadowColor = *textPrinter->printerTemplate.currentChar;
|
||||
textPrinter->printerTemplate.currentChar++;
|
||||
GenerateFontHalfRowLookupTable(textPrinter->printerTemplate.fgColor, textPrinter->printerTemplate.bgColor, textPrinter->printerTemplate.shadowColor);
|
||||
return RENDER_REPEAT;
|
||||
case EXT_CTRL_CODE_PALETTE:
|
||||
textPrinter->printerTemplate.currentChar++;
|
||||
return RENDER_REPEAT;
|
||||
case EXT_CTRL_CODE_FONT:
|
||||
subStruct->fontId = *textPrinter->printerTemplate.currentChar;
|
||||
textPrinter->printerTemplate.currentChar++;
|
||||
return RENDER_REPEAT;
|
||||
case EXT_CTRL_CODE_RESET_SIZE:
|
||||
return RENDER_REPEAT;
|
||||
case EXT_CTRL_CODE_PAUSE:
|
||||
textPrinter->delayCounter = *textPrinter->printerTemplate.currentChar;
|
||||
textPrinter->printerTemplate.currentChar++;
|
||||
textPrinter->state = RENDER_STATE_PAUSE;
|
||||
return RENDER_REPEAT;
|
||||
case EXT_CTRL_CODE_PAUSE_UNTIL_PRESS:
|
||||
textPrinter->state = RENDER_STATE_WAIT;
|
||||
if (gTextFlags.autoScroll)
|
||||
subStruct->autoScrollDelay = 0;
|
||||
return RENDER_UPDATE;
|
||||
case EXT_CTRL_CODE_WAIT_SE:
|
||||
textPrinter->state = RENDER_STATE_WAIT_SE;
|
||||
return RENDER_UPDATE;
|
||||
case EXT_CTRL_CODE_PLAY_BGM:
|
||||
currChar = *textPrinter->printerTemplate.currentChar;
|
||||
textPrinter->printerTemplate.currentChar++;
|
||||
currChar |= *textPrinter->printerTemplate.currentChar << 8;
|
||||
textPrinter->printerTemplate.currentChar++;
|
||||
PlayBGM(currChar);
|
||||
return RENDER_REPEAT;
|
||||
case EXT_CTRL_CODE_ESCAPE:
|
||||
currChar = *textPrinter->printerTemplate.currentChar | 0x100;
|
||||
textPrinter->printerTemplate.currentChar++;
|
||||
break;
|
||||
case EXT_CTRL_CODE_PLAY_SE:
|
||||
currChar = *textPrinter->printerTemplate.currentChar;
|
||||
textPrinter->printerTemplate.currentChar++;
|
||||
currChar |= (*textPrinter->printerTemplate.currentChar << 8);
|
||||
textPrinter->printerTemplate.currentChar++;
|
||||
PlaySE(currChar);
|
||||
return RENDER_REPEAT;
|
||||
case EXT_CTRL_CODE_SHIFT_TEXT:
|
||||
textPrinter->printerTemplate.currentX = textPrinter->printerTemplate.x + *textPrinter->printerTemplate.currentChar;
|
||||
textPrinter->printerTemplate.currentChar++;
|
||||
return RENDER_REPEAT;
|
||||
case EXT_CTRL_CODE_SHIFT_DOWN:
|
||||
textPrinter->printerTemplate.currentY = textPrinter->printerTemplate.y + *textPrinter->printerTemplate.currentChar;
|
||||
textPrinter->printerTemplate.currentChar++;
|
||||
return RENDER_REPEAT;
|
||||
case EXT_CTRL_CODE_FILL_WINDOW:
|
||||
FillWindowPixelBuffer(textPrinter->printerTemplate.windowId, PIXEL_FILL(textPrinter->printerTemplate.bgColor));
|
||||
textPrinter->printerTemplate.currentX = textPrinter->printerTemplate.x;
|
||||
textPrinter->printerTemplate.currentY = textPrinter->printerTemplate.y;
|
||||
return RENDER_REPEAT;
|
||||
case EXT_CTRL_CODE_PAUSE_MUSIC:
|
||||
m4aMPlayStop(&gMPlayInfo_BGM);
|
||||
return RENDER_REPEAT;
|
||||
case EXT_CTRL_CODE_RESUME_MUSIC:
|
||||
m4aMPlayContinue(&gMPlayInfo_BGM);
|
||||
return RENDER_REPEAT;
|
||||
case EXT_CTRL_CODE_CLEAR:
|
||||
width = *textPrinter->printerTemplate.currentChar;
|
||||
textPrinter->printerTemplate.currentChar++;
|
||||
if (width > 0)
|
||||
{
|
||||
ClearTextSpan(textPrinter, width);
|
||||
textPrinter->printerTemplate.currentX += width;
|
||||
return RENDER_PRINT;
|
||||
}
|
||||
return RENDER_REPEAT;
|
||||
case EXT_CTRL_CODE_SKIP:
|
||||
textPrinter->printerTemplate.currentX = *textPrinter->printerTemplate.currentChar + textPrinter->printerTemplate.x;
|
||||
textPrinter->printerTemplate.currentChar++;
|
||||
return RENDER_REPEAT;
|
||||
case EXT_CTRL_CODE_CLEAR_TO:
|
||||
{
|
||||
widthHelper = *textPrinter->printerTemplate.currentChar;
|
||||
widthHelper += textPrinter->printerTemplate.x;
|
||||
textPrinter->printerTemplate.currentChar++;
|
||||
width = widthHelper - textPrinter->printerTemplate.currentX;
|
||||
if (width > 0)
|
||||
{
|
||||
ClearTextSpan(textPrinter, width);
|
||||
textPrinter->printerTemplate.currentX += width;
|
||||
return RENDER_PRINT;
|
||||
}
|
||||
}
|
||||
return RENDER_REPEAT;
|
||||
case EXT_CTRL_CODE_MIN_LETTER_SPACING:
|
||||
textPrinter->minLetterSpacing = *textPrinter->printerTemplate.currentChar++;
|
||||
return RENDER_REPEAT;
|
||||
case EXT_CTRL_CODE_JPN:
|
||||
textPrinter->japanese = TRUE;
|
||||
return RENDER_REPEAT;
|
||||
case EXT_CTRL_CODE_ENG:
|
||||
textPrinter->japanese = FALSE;
|
||||
return RENDER_REPEAT;
|
||||
}
|
||||
break;
|
||||
case CHAR_PROMPT_CLEAR:
|
||||
textPrinter->state = RENDER_STATE_CLEAR;
|
||||
TextPrinterInitDownArrowCounters(textPrinter);
|
||||
return RENDER_UPDATE;
|
||||
case CHAR_PROMPT_SCROLL:
|
||||
textPrinter->state = RENDER_STATE_SCROLL_START;
|
||||
TextPrinterInitDownArrowCounters(textPrinter);
|
||||
return RENDER_UPDATE;
|
||||
case CHAR_EXTRA_SYMBOL:
|
||||
currChar = *textPrinter->printerTemplate.currentChar | 0x100;
|
||||
textPrinter->printerTemplate.currentChar++;
|
||||
break;
|
||||
case CHAR_KEYPAD_ICON:
|
||||
currChar = *textPrinter->printerTemplate.currentChar++;
|
||||
gCurGlyph.width = DrawKeypadIcon(textPrinter->printerTemplate.windowId, currChar, textPrinter->printerTemplate.currentX, textPrinter->printerTemplate.currentY);
|
||||
textPrinter->printerTemplate.currentX += gCurGlyph.width + textPrinter->printerTemplate.letterSpacing;
|
||||
return RENDER_PRINT;
|
||||
case EOS:
|
||||
return RENDER_FINISH;
|
||||
}
|
||||
|
||||
switch (subStruct->fontId)
|
||||
{
|
||||
case FONT_SMALL:
|
||||
DecompressGlyph_Small(currChar, textPrinter->japanese);
|
||||
break;
|
||||
case FONT_NORMAL:
|
||||
DecompressGlyph_Normal(currChar, textPrinter->japanese);
|
||||
break;
|
||||
case FONT_SHORT:
|
||||
case FONT_SHORT_COPY_1:
|
||||
case FONT_SHORT_COPY_2:
|
||||
case FONT_SHORT_COPY_3:
|
||||
DecompressGlyph_Short(currChar, textPrinter->japanese);
|
||||
break;
|
||||
case FONT_NARROW:
|
||||
DecompressGlyph_Narrow(currChar, textPrinter->japanese);
|
||||
break;
|
||||
case FONT_SMALL_NARROW:
|
||||
DecompressGlyph_SmallNarrow(currChar, textPrinter->japanese);
|
||||
break;
|
||||
case FONT_BRAILLE:
|
||||
break;
|
||||
}
|
||||
|
||||
CopyGlyphToWindow(textPrinter);
|
||||
|
||||
if (textPrinter->minLetterSpacing)
|
||||
{
|
||||
textPrinter->printerTemplate.currentX += gCurGlyph.width;
|
||||
width = textPrinter->minLetterSpacing - gCurGlyph.width;
|
||||
if (width > 0)
|
||||
{
|
||||
ClearTextSpan(textPrinter, width);
|
||||
textPrinter->printerTemplate.currentX += width;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (textPrinter->japanese)
|
||||
textPrinter->printerTemplate.currentX += (gCurGlyph.width + textPrinter->printerTemplate.letterSpacing);
|
||||
else
|
||||
textPrinter->printerTemplate.currentX += gCurGlyph.width;
|
||||
}
|
||||
return RENDER_PRINT;
|
||||
case RENDER_STATE_WAIT:
|
||||
if (TextPrinterWait(textPrinter))
|
||||
textPrinter->state = RENDER_STATE_HANDLE_CHAR;
|
||||
return RENDER_UPDATE;
|
||||
case RENDER_STATE_CLEAR:
|
||||
if (TextPrinterWaitWithDownArrow(textPrinter))
|
||||
{
|
||||
FillWindowPixelBuffer(textPrinter->printerTemplate.windowId, PIXEL_FILL(textPrinter->printerTemplate.bgColor));
|
||||
textPrinter->printerTemplate.currentX = textPrinter->printerTemplate.x;
|
||||
textPrinter->printerTemplate.currentY = textPrinter->printerTemplate.y;
|
||||
textPrinter->state = RENDER_STATE_HANDLE_CHAR;
|
||||
}
|
||||
return RENDER_UPDATE;
|
||||
case RENDER_STATE_SCROLL_START:
|
||||
if (TextPrinterWaitWithDownArrow(textPrinter))
|
||||
{
|
||||
TextPrinterClearDownArrow(textPrinter);
|
||||
textPrinter->scrollDistance = gFonts[textPrinter->printerTemplate.fontId].maxLetterHeight + textPrinter->printerTemplate.lineSpacing;
|
||||
textPrinter->printerTemplate.currentX = textPrinter->printerTemplate.x;
|
||||
textPrinter->state = RENDER_STATE_SCROLL;
|
||||
}
|
||||
return RENDER_UPDATE;
|
||||
case RENDER_STATE_SCROLL:
|
||||
if (textPrinter->scrollDistance)
|
||||
{
|
||||
int scrollSpeed = GetPlayerTextSpeed();
|
||||
int speed = sWindowVerticalScrollSpeeds[scrollSpeed];
|
||||
if (textPrinter->scrollDistance < speed)
|
||||
{
|
||||
ScrollWindow(textPrinter->printerTemplate.windowId, 0, textPrinter->scrollDistance, PIXEL_FILL(textPrinter->printerTemplate.bgColor));
|
||||
textPrinter->scrollDistance = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ScrollWindow(textPrinter->printerTemplate.windowId, 0, speed, PIXEL_FILL(textPrinter->printerTemplate.bgColor));
|
||||
textPrinter->scrollDistance -= speed;
|
||||
}
|
||||
CopyWindowToVram(textPrinter->printerTemplate.windowId, COPYWIN_GFX);
|
||||
}
|
||||
else
|
||||
{
|
||||
textPrinter->state = RENDER_STATE_HANDLE_CHAR;
|
||||
}
|
||||
return RENDER_UPDATE;
|
||||
case RENDER_STATE_WAIT_SE:
|
||||
if (!IsSEPlaying())
|
||||
textPrinter->state = RENDER_STATE_HANDLE_CHAR;
|
||||
return RENDER_UPDATE;
|
||||
case RENDER_STATE_PAUSE:
|
||||
if (textPrinter->delayCounter != 0)
|
||||
textPrinter->delayCounter--;
|
||||
else
|
||||
textPrinter->state = RENDER_STATE_HANDLE_CHAR;
|
||||
return RENDER_UPDATE;
|
||||
}
|
||||
|
||||
return RENDER_FINISH;
|
||||
}
|
||||
|
||||
// Unused
|
||||
static u32 GetStringWidthFixedWidthFont(const u8 *str, u8 fontId, u8 letterSpacing)
|
||||
{
|
||||
int i;
|
||||
u8 width;
|
||||
int temp;
|
||||
int temp2;
|
||||
u8 line;
|
||||
int strPos;
|
||||
u8 lineWidths[8];
|
||||
const u8 *strLocal;
|
||||
|
||||
for (i = 0; i < (int)ARRAY_COUNT(lineWidths); i++)
|
||||
lineWidths[i] = 0;
|
||||
|
||||
width = 0;
|
||||
line = 0;
|
||||
strLocal = str;
|
||||
strPos = 0;
|
||||
|
||||
do
|
||||
{
|
||||
temp = strLocal[strPos++];
|
||||
switch (temp)
|
||||
{
|
||||
case CHAR_NEWLINE:
|
||||
case EOS:
|
||||
lineWidths[line] = width;
|
||||
width = 0;
|
||||
line++;
|
||||
break;
|
||||
case EXT_CTRL_CODE_BEGIN:
|
||||
temp2 = strLocal[strPos++];
|
||||
switch (temp2)
|
||||
{
|
||||
case EXT_CTRL_CODE_COLOR_HIGHLIGHT_SHADOW:
|
||||
++strPos;
|
||||
case EXT_CTRL_CODE_PLAY_BGM:
|
||||
case EXT_CTRL_CODE_PLAY_SE:
|
||||
++strPos;
|
||||
case EXT_CTRL_CODE_COLOR:
|
||||
case EXT_CTRL_CODE_HIGHLIGHT:
|
||||
case EXT_CTRL_CODE_SHADOW:
|
||||
case EXT_CTRL_CODE_PALETTE:
|
||||
case EXT_CTRL_CODE_FONT:
|
||||
case EXT_CTRL_CODE_PAUSE:
|
||||
case EXT_CTRL_CODE_ESCAPE:
|
||||
case EXT_CTRL_CODE_SHIFT_TEXT:
|
||||
case EXT_CTRL_CODE_SHIFT_DOWN:
|
||||
case EXT_CTRL_CODE_CLEAR:
|
||||
case EXT_CTRL_CODE_SKIP:
|
||||
case EXT_CTRL_CODE_CLEAR_TO:
|
||||
case EXT_CTRL_CODE_MIN_LETTER_SPACING:
|
||||
++strPos;
|
||||
break;
|
||||
case EXT_CTRL_CODE_RESET_SIZE:
|
||||
case EXT_CTRL_CODE_PAUSE_UNTIL_PRESS:
|
||||
case EXT_CTRL_CODE_WAIT_SE:
|
||||
case EXT_CTRL_CODE_FILL_WINDOW:
|
||||
case EXT_CTRL_CODE_JPN:
|
||||
case EXT_CTRL_CODE_ENG:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case CHAR_DYNAMIC:
|
||||
case PLACEHOLDER_BEGIN:
|
||||
++strPos;
|
||||
break;
|
||||
case CHAR_PROMPT_SCROLL:
|
||||
case CHAR_PROMPT_CLEAR:
|
||||
break;
|
||||
case CHAR_KEYPAD_ICON:
|
||||
case CHAR_EXTRA_SYMBOL:
|
||||
++strPos;
|
||||
default:
|
||||
++width;
|
||||
break;
|
||||
}
|
||||
} while (temp != EOS);
|
||||
|
||||
for (width = 0, strPos = 0; strPos < 8; ++strPos)
|
||||
{
|
||||
if (width < lineWidths[strPos])
|
||||
width = lineWidths[strPos];
|
||||
}
|
||||
|
||||
return (u8)(GetFontAttribute(fontId, FONTATTR_MAX_LETTER_WIDTH) + letterSpacing) * width;
|
||||
}
|
||||
|
||||
static u32 (*GetFontWidthFunc(u8 fontId))(u16, bool32)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < ARRAY_COUNT(sGlyphWidthFuncs); ++i)
|
||||
{
|
||||
if (fontId == sGlyphWidthFuncs[i].fontId)
|
||||
return sGlyphWidthFuncs[i].func;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s32 GetStringWidth(u8 fontId, const u8 *str, s16 letterSpacing)
|
||||
{
|
||||
bool8 isJapanese;
|
||||
int minGlyphWidth;
|
||||
u32 (*func)(u16 fontId, bool32 isJapanese);
|
||||
int localLetterSpacing;
|
||||
u32 lineWidth;
|
||||
const u8 *bufferPointer;
|
||||
int glyphWidth;
|
||||
s32 width;
|
||||
|
||||
isJapanese = 0;
|
||||
minGlyphWidth = 0;
|
||||
|
||||
func = GetFontWidthFunc(fontId);
|
||||
if (func == NULL)
|
||||
return 0;
|
||||
|
||||
if (letterSpacing == -1)
|
||||
localLetterSpacing = GetFontAttribute(fontId, FONTATTR_LETTER_SPACING);
|
||||
else
|
||||
localLetterSpacing = letterSpacing;
|
||||
|
||||
width = 0;
|
||||
lineWidth = 0;
|
||||
bufferPointer = 0;
|
||||
|
||||
while (*str != EOS)
|
||||
{
|
||||
switch (*str)
|
||||
{
|
||||
case CHAR_NEWLINE:
|
||||
if (lineWidth > width)
|
||||
width = lineWidth;
|
||||
lineWidth = 0;
|
||||
break;
|
||||
case PLACEHOLDER_BEGIN:
|
||||
switch (*++str)
|
||||
{
|
||||
case PLACEHOLDER_ID_STRING_VAR_1:
|
||||
bufferPointer = gStringVar1;
|
||||
break;
|
||||
case PLACEHOLDER_ID_STRING_VAR_2:
|
||||
bufferPointer = gStringVar2;
|
||||
break;
|
||||
case PLACEHOLDER_ID_STRING_VAR_3:
|
||||
bufferPointer = gStringVar3;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
case CHAR_DYNAMIC:
|
||||
if (bufferPointer == NULL)
|
||||
bufferPointer = DynamicPlaceholderTextUtil_GetPlaceholderPtr(*++str);
|
||||
while (*bufferPointer != EOS)
|
||||
{
|
||||
glyphWidth = func(*bufferPointer++, isJapanese);
|
||||
if (minGlyphWidth > 0)
|
||||
{
|
||||
if (glyphWidth < minGlyphWidth)
|
||||
glyphWidth = minGlyphWidth;
|
||||
lineWidth += glyphWidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
lineWidth += glyphWidth;
|
||||
if (isJapanese && str[1] != EOS)
|
||||
lineWidth += localLetterSpacing;
|
||||
}
|
||||
}
|
||||
bufferPointer = 0;
|
||||
break;
|
||||
case EXT_CTRL_CODE_BEGIN:
|
||||
switch (*++str)
|
||||
{
|
||||
case EXT_CTRL_CODE_COLOR_HIGHLIGHT_SHADOW:
|
||||
++str;
|
||||
case EXT_CTRL_CODE_PLAY_BGM:
|
||||
case EXT_CTRL_CODE_PLAY_SE:
|
||||
++str;
|
||||
case EXT_CTRL_CODE_COLOR:
|
||||
case EXT_CTRL_CODE_HIGHLIGHT:
|
||||
case EXT_CTRL_CODE_SHADOW:
|
||||
case EXT_CTRL_CODE_PALETTE:
|
||||
case EXT_CTRL_CODE_PAUSE:
|
||||
case EXT_CTRL_CODE_ESCAPE:
|
||||
case EXT_CTRL_CODE_SHIFT_TEXT:
|
||||
case EXT_CTRL_CODE_SHIFT_DOWN:
|
||||
++str;
|
||||
break;
|
||||
case EXT_CTRL_CODE_FONT:
|
||||
func = GetFontWidthFunc(*++str);
|
||||
if (func == NULL)
|
||||
return 0;
|
||||
if (letterSpacing == -1)
|
||||
localLetterSpacing = GetFontAttribute(*str, FONTATTR_LETTER_SPACING);
|
||||
break;
|
||||
case EXT_CTRL_CODE_CLEAR:
|
||||
glyphWidth = *++str;
|
||||
lineWidth += glyphWidth;
|
||||
break;
|
||||
case EXT_CTRL_CODE_SKIP:
|
||||
lineWidth = *++str;
|
||||
break;
|
||||
case EXT_CTRL_CODE_CLEAR_TO:
|
||||
if (*++str > lineWidth)
|
||||
lineWidth = *str;
|
||||
break;
|
||||
case EXT_CTRL_CODE_MIN_LETTER_SPACING:
|
||||
minGlyphWidth = *++str;
|
||||
break;
|
||||
case EXT_CTRL_CODE_JPN:
|
||||
isJapanese = 1;
|
||||
break;
|
||||
case EXT_CTRL_CODE_ENG:
|
||||
isJapanese = 0;
|
||||
break;
|
||||
case EXT_CTRL_CODE_RESET_SIZE:
|
||||
case EXT_CTRL_CODE_PAUSE_UNTIL_PRESS:
|
||||
case EXT_CTRL_CODE_WAIT_SE:
|
||||
case EXT_CTRL_CODE_FILL_WINDOW:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case CHAR_KEYPAD_ICON:
|
||||
case CHAR_EXTRA_SYMBOL:
|
||||
if (*str == CHAR_EXTRA_SYMBOL)
|
||||
glyphWidth = func(*++str | 0x100, isJapanese);
|
||||
else
|
||||
glyphWidth = GetKeypadIconWidth(*++str);
|
||||
|
||||
if (minGlyphWidth > 0)
|
||||
{
|
||||
if (glyphWidth < minGlyphWidth)
|
||||
glyphWidth = minGlyphWidth;
|
||||
lineWidth += glyphWidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
lineWidth += glyphWidth;
|
||||
if (isJapanese && str[1] != EOS)
|
||||
lineWidth += localLetterSpacing;
|
||||
}
|
||||
break;
|
||||
case CHAR_PROMPT_SCROLL:
|
||||
case CHAR_PROMPT_CLEAR:
|
||||
break;
|
||||
default:
|
||||
glyphWidth = func(*str, isJapanese);
|
||||
if (minGlyphWidth > 0)
|
||||
{
|
||||
if (glyphWidth < minGlyphWidth)
|
||||
glyphWidth = minGlyphWidth;
|
||||
lineWidth += glyphWidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
lineWidth += glyphWidth;
|
||||
if (isJapanese && str[1] != EOS)
|
||||
lineWidth += localLetterSpacing;
|
||||
}
|
||||
break;
|
||||
}
|
||||
++str;
|
||||
}
|
||||
|
||||
if (lineWidth > width)
|
||||
return lineWidth;
|
||||
return width;
|
||||
}
|
||||
|
||||
u8 RenderTextHandleBold(u8 *pixels, u8 fontId, u8 *str)
|
||||
{
|
||||
u8 shadowColor;
|
||||
u8 *strLocal;
|
||||
int strPos;
|
||||
int temp;
|
||||
int temp2;
|
||||
u8 colorBackup[3];
|
||||
u8 fgColor;
|
||||
u8 bgColor;
|
||||
|
||||
SaveTextColors(&colorBackup[0], &colorBackup[1], &colorBackup[2]);
|
||||
|
||||
fgColor = TEXT_COLOR_WHITE;
|
||||
bgColor = TEXT_COLOR_TRANSPARENT;
|
||||
shadowColor = TEXT_COLOR_LIGHT_GRAY;
|
||||
|
||||
GenerateFontHalfRowLookupTable(TEXT_COLOR_WHITE, TEXT_COLOR_TRANSPARENT, TEXT_COLOR_LIGHT_GRAY);
|
||||
strLocal = str;
|
||||
strPos = 0;
|
||||
|
||||
do
|
||||
{
|
||||
temp = strLocal[strPos++];
|
||||
switch (temp)
|
||||
{
|
||||
case EXT_CTRL_CODE_BEGIN:
|
||||
temp2 = strLocal[strPos++];
|
||||
switch (temp2)
|
||||
{
|
||||
case EXT_CTRL_CODE_COLOR_HIGHLIGHT_SHADOW:
|
||||
fgColor = strLocal[strPos++];
|
||||
bgColor = strLocal[strPos++];
|
||||
shadowColor = strLocal[strPos++];
|
||||
GenerateFontHalfRowLookupTable(fgColor, bgColor, shadowColor);
|
||||
continue;
|
||||
case EXT_CTRL_CODE_COLOR:
|
||||
fgColor = strLocal[strPos++];
|
||||
GenerateFontHalfRowLookupTable(fgColor, bgColor, shadowColor);
|
||||
continue;
|
||||
case EXT_CTRL_CODE_HIGHLIGHT:
|
||||
bgColor = strLocal[strPos++];
|
||||
GenerateFontHalfRowLookupTable(fgColor, bgColor, shadowColor);
|
||||
continue;
|
||||
case EXT_CTRL_CODE_SHADOW:
|
||||
shadowColor = strLocal[strPos++];
|
||||
GenerateFontHalfRowLookupTable(fgColor, bgColor, shadowColor);
|
||||
continue;
|
||||
case EXT_CTRL_CODE_FONT:
|
||||
fontId = strLocal[strPos++];
|
||||
break;
|
||||
case EXT_CTRL_CODE_PLAY_BGM:
|
||||
case EXT_CTRL_CODE_PLAY_SE:
|
||||
++strPos;
|
||||
case EXT_CTRL_CODE_PALETTE:
|
||||
case EXT_CTRL_CODE_PAUSE:
|
||||
case EXT_CTRL_CODE_ESCAPE:
|
||||
case EXT_CTRL_CODE_SHIFT_TEXT:
|
||||
case EXT_CTRL_CODE_SHIFT_DOWN:
|
||||
case EXT_CTRL_CODE_CLEAR:
|
||||
case EXT_CTRL_CODE_SKIP:
|
||||
case EXT_CTRL_CODE_CLEAR_TO:
|
||||
case EXT_CTRL_CODE_MIN_LETTER_SPACING:
|
||||
++strPos;
|
||||
break;
|
||||
case EXT_CTRL_CODE_RESET_SIZE:
|
||||
case EXT_CTRL_CODE_PAUSE_UNTIL_PRESS:
|
||||
case EXT_CTRL_CODE_WAIT_SE:
|
||||
case EXT_CTRL_CODE_FILL_WINDOW:
|
||||
case EXT_CTRL_CODE_JPN:
|
||||
case EXT_CTRL_CODE_ENG:
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case CHAR_DYNAMIC:
|
||||
case CHAR_KEYPAD_ICON:
|
||||
case CHAR_EXTRA_SYMBOL:
|
||||
case PLACEHOLDER_BEGIN:
|
||||
++strPos;
|
||||
break;
|
||||
case CHAR_PROMPT_SCROLL:
|
||||
case CHAR_PROMPT_CLEAR:
|
||||
case CHAR_NEWLINE:
|
||||
case EOS:
|
||||
break;
|
||||
default:
|
||||
switch (fontId)
|
||||
{
|
||||
case FONT_BOLD:
|
||||
DecompressGlyph_Bold(temp);
|
||||
break;
|
||||
case FONT_NORMAL:
|
||||
default:
|
||||
DecompressGlyph_Normal(temp, TRUE);
|
||||
break;
|
||||
}
|
||||
CpuCopy32(gCurGlyph.gfxBufferTop, pixels, 0x20);
|
||||
CpuCopy32(gCurGlyph.gfxBufferBottom, pixels + 0x20, 0x20);
|
||||
pixels += 0x40;
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (temp != EOS);
|
||||
|
||||
RestoreTextColors(&colorBackup[0], &colorBackup[1], &colorBackup[2]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
u8 DrawKeypadIcon(u8 windowId, u8 keypadIconId, u16 x, u16 y)
|
||||
{
|
||||
BlitBitmapRectToWindow(
|
||||
windowId,
|
||||
sKeypadIconTiles + (sKeypadIcons[keypadIconId].tileOffset * 0x20),
|
||||
0,
|
||||
0,
|
||||
0x80,
|
||||
0x80,
|
||||
x,
|
||||
y,
|
||||
sKeypadIcons[keypadIconId].width,
|
||||
sKeypadIcons[keypadIconId].height);
|
||||
return sKeypadIcons[keypadIconId].width;
|
||||
}
|
||||
|
||||
u8 GetKeypadIconTileOffset(u8 keypadIconId)
|
||||
{
|
||||
return sKeypadIcons[keypadIconId].tileOffset;
|
||||
}
|
||||
|
||||
u8 GetKeypadIconWidth(u8 keypadIconId)
|
||||
{
|
||||
return sKeypadIcons[keypadIconId].width;
|
||||
}
|
||||
|
||||
u8 GetKeypadIconHeight(u8 keypadIconId)
|
||||
{
|
||||
return sKeypadIcons[keypadIconId].height;
|
||||
}
|
||||
|
||||
void SetDefaultFontsPointer(void)
|
||||
{
|
||||
SetFontsPointer(sFontInfos);
|
||||
}
|
||||
|
||||
u8 GetFontAttribute(u8 fontId, u8 attributeId)
|
||||
{
|
||||
int result = 0;
|
||||
switch (attributeId)
|
||||
{
|
||||
case FONTATTR_MAX_LETTER_WIDTH:
|
||||
result = sFontInfos[fontId].maxLetterWidth;
|
||||
break;
|
||||
case FONTATTR_MAX_LETTER_HEIGHT:
|
||||
result = sFontInfos[fontId].maxLetterHeight;
|
||||
break;
|
||||
case FONTATTR_LETTER_SPACING:
|
||||
result = sFontInfos[fontId].letterSpacing;
|
||||
break;
|
||||
case FONTATTR_LINE_SPACING:
|
||||
result = sFontInfos[fontId].lineSpacing;
|
||||
break;
|
||||
case FONTATTR_UNKNOWN:
|
||||
result = sFontInfos[fontId].unk;
|
||||
break;
|
||||
case FONTATTR_COLOR_FOREGROUND:
|
||||
result = sFontInfos[fontId].fgColor;
|
||||
break;
|
||||
case FONTATTR_COLOR_BACKGROUND:
|
||||
result = sFontInfos[fontId].bgColor;
|
||||
break;
|
||||
case FONTATTR_COLOR_SHADOW:
|
||||
result = sFontInfos[fontId].shadowColor;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
u8 GetMenuCursorDimensionByFont(u8 fontId, u8 whichDimension)
|
||||
{
|
||||
return sMenuCursorDimensions[fontId][whichDimension];
|
||||
}
|
||||
|
||||
static void DecompressGlyph_Small(u16 glyphId, bool32 isJapanese)
|
||||
{
|
||||
const u16* glyphs;
|
||||
|
||||
if (isJapanese == 1)
|
||||
{
|
||||
glyphs = gFontSmallJapaneseGlyphs + (0x100 * (glyphId >> 0x4)) + (0x8 * (glyphId & 0xF));
|
||||
DecompressGlyphTile(glyphs, gCurGlyph.gfxBufferTop);
|
||||
DecompressGlyphTile(glyphs + 0x80, gCurGlyph.gfxBufferBottom);
|
||||
gCurGlyph.width = 8;
|
||||
gCurGlyph.height = 12;
|
||||
}
|
||||
else
|
||||
{
|
||||
glyphs = gFontSmallLatinGlyphs + (0x20 * glyphId);
|
||||
gCurGlyph.width = gFontSmallLatinGlyphWidths[glyphId];
|
||||
|
||||
if (gCurGlyph.width <= 8)
|
||||
{
|
||||
DecompressGlyphTile(glyphs, gCurGlyph.gfxBufferTop);
|
||||
DecompressGlyphTile(glyphs + 0x10, gCurGlyph.gfxBufferBottom);
|
||||
}
|
||||
else
|
||||
{
|
||||
DecompressGlyphTile(glyphs, gCurGlyph.gfxBufferTop);
|
||||
DecompressGlyphTile(glyphs + 0x8, gCurGlyph.gfxBufferTop + 8);
|
||||
DecompressGlyphTile(glyphs + 0x10, gCurGlyph.gfxBufferBottom);
|
||||
DecompressGlyphTile(glyphs + 0x18, gCurGlyph.gfxBufferBottom + 8);
|
||||
}
|
||||
|
||||
gCurGlyph.height = 13;
|
||||
}
|
||||
}
|
||||
|
||||
static u32 GetGlyphWidth_Small(u16 glyphId, bool32 isJapanese)
|
||||
{
|
||||
if (isJapanese == TRUE)
|
||||
return 8;
|
||||
else
|
||||
return gFontSmallLatinGlyphWidths[glyphId];
|
||||
}
|
||||
|
||||
static void DecompressGlyph_Narrow(u16 glyphId, bool32 isJapanese)
|
||||
{
|
||||
const u16* glyphs;
|
||||
|
||||
if (isJapanese == TRUE)
|
||||
{
|
||||
glyphs = gFontNormalJapaneseGlyphs + (0x100 * (glyphId >> 0x4)) + (0x8 * (glyphId % 0x10));
|
||||
DecompressGlyphTile(glyphs, gCurGlyph.gfxBufferTop);
|
||||
DecompressGlyphTile(glyphs + 0x80, gCurGlyph.gfxBufferBottom);
|
||||
gCurGlyph.width = 8;
|
||||
gCurGlyph.height = 15;
|
||||
}
|
||||
else
|
||||
{
|
||||
glyphs = gFontNarrowLatinGlyphs + (0x20 * glyphId);
|
||||
gCurGlyph.width = gFontNarrowLatinGlyphWidths[glyphId];
|
||||
|
||||
if (gCurGlyph.width <= 8)
|
||||
{
|
||||
DecompressGlyphTile(glyphs, gCurGlyph.gfxBufferTop);
|
||||
DecompressGlyphTile(glyphs + 0x10, gCurGlyph.gfxBufferBottom);
|
||||
}
|
||||
else
|
||||
{
|
||||
DecompressGlyphTile(glyphs, gCurGlyph.gfxBufferTop);
|
||||
DecompressGlyphTile(glyphs + 0x8, gCurGlyph.gfxBufferTop + 8);
|
||||
DecompressGlyphTile(glyphs + 0x10, gCurGlyph.gfxBufferBottom);
|
||||
DecompressGlyphTile(glyphs + 0x18, gCurGlyph.gfxBufferBottom + 8);
|
||||
}
|
||||
|
||||
gCurGlyph.height = 15;
|
||||
}
|
||||
}
|
||||
|
||||
static u32 GetGlyphWidth_Narrow(u16 glyphId, bool32 isJapanese)
|
||||
{
|
||||
if (isJapanese == TRUE)
|
||||
return 8;
|
||||
else
|
||||
return gFontNarrowLatinGlyphWidths[glyphId];
|
||||
}
|
||||
|
||||
static void DecompressGlyph_SmallNarrow(u16 glyphId, bool32 isJapanese)
|
||||
{
|
||||
const u16* glyphs;
|
||||
|
||||
if (isJapanese == TRUE)
|
||||
{
|
||||
glyphs = gFontSmallJapaneseGlyphs + (0x100 * (glyphId >> 0x4)) + (0x8 * (glyphId & 0xF));
|
||||
DecompressGlyphTile(glyphs, gCurGlyph.gfxBufferTop);
|
||||
DecompressGlyphTile(glyphs + 0x80, gCurGlyph.gfxBufferBottom);
|
||||
gCurGlyph.width = 8;
|
||||
gCurGlyph.height = 12;
|
||||
}
|
||||
else
|
||||
{
|
||||
glyphs = gFontSmallNarrowLatinGlyphs + (0x20 * glyphId);
|
||||
gCurGlyph.width = gFontSmallNarrowLatinGlyphWidths[glyphId];
|
||||
|
||||
if (gCurGlyph.width <= 8)
|
||||
{
|
||||
DecompressGlyphTile(glyphs, gCurGlyph.gfxBufferTop);
|
||||
DecompressGlyphTile(glyphs + 0x10, gCurGlyph.gfxBufferBottom);
|
||||
}
|
||||
else
|
||||
{
|
||||
DecompressGlyphTile(glyphs, gCurGlyph.gfxBufferTop);
|
||||
DecompressGlyphTile(glyphs + 0x8, gCurGlyph.gfxBufferTop + 8);
|
||||
DecompressGlyphTile(glyphs + 0x10, gCurGlyph.gfxBufferBottom);
|
||||
DecompressGlyphTile(glyphs + 0x18, gCurGlyph.gfxBufferBottom + 8);
|
||||
}
|
||||
|
||||
gCurGlyph.height = 12;
|
||||
}
|
||||
}
|
||||
|
||||
static u32 GetGlyphWidth_SmallNarrow(u16 glyphId, bool32 isJapanese)
|
||||
{
|
||||
if (isJapanese == TRUE)
|
||||
return 8;
|
||||
else
|
||||
return gFontSmallNarrowLatinGlyphWidths[glyphId];
|
||||
}
|
||||
|
||||
static void DecompressGlyph_Short(u16 glyphId, bool32 isJapanese)
|
||||
{
|
||||
const u16* glyphs;
|
||||
|
||||
if (isJapanese == TRUE)
|
||||
{
|
||||
glyphs = gFontShortJapaneseGlyphs + (0x100 * (glyphId >> 0x3)) + (0x10 * (glyphId & 0x7));
|
||||
DecompressGlyphTile(glyphs, gCurGlyph.gfxBufferTop);
|
||||
DecompressGlyphTile(glyphs + 0x8, gCurGlyph.gfxBufferTop + 8);
|
||||
DecompressGlyphTile(glyphs + 0x80, gCurGlyph.gfxBufferBottom); // gCurGlyph + 0x20
|
||||
DecompressGlyphTile(glyphs + 0x88, gCurGlyph.gfxBufferBottom + 8); // gCurGlyph + 0x60
|
||||
gCurGlyph.width = gFontShortJapaneseGlyphWidths[glyphId];
|
||||
gCurGlyph.height = 14;
|
||||
}
|
||||
else
|
||||
{
|
||||
glyphs = gFontShortLatinGlyphs + (0x20 * glyphId);
|
||||
gCurGlyph.width = gFontShortLatinGlyphWidths[glyphId];
|
||||
|
||||
if (gCurGlyph.width <= 8)
|
||||
{
|
||||
DecompressGlyphTile(glyphs, gCurGlyph.gfxBufferTop);
|
||||
DecompressGlyphTile(glyphs + 0x10, gCurGlyph.gfxBufferBottom);
|
||||
}
|
||||
else
|
||||
{
|
||||
DecompressGlyphTile(glyphs, gCurGlyph.gfxBufferTop);
|
||||
DecompressGlyphTile(glyphs + 0x8, gCurGlyph.gfxBufferTop + 8);
|
||||
DecompressGlyphTile(glyphs + 0x10, gCurGlyph.gfxBufferBottom);
|
||||
DecompressGlyphTile(glyphs + 0x18, gCurGlyph.gfxBufferBottom + 8);
|
||||
}
|
||||
|
||||
gCurGlyph.height = 14;
|
||||
}
|
||||
}
|
||||
|
||||
static u32 GetGlyphWidth_Short(u16 glyphId, bool32 isJapanese)
|
||||
{
|
||||
if (isJapanese == TRUE)
|
||||
return gFontShortJapaneseGlyphWidths[glyphId];
|
||||
else
|
||||
return gFontShortLatinGlyphWidths[glyphId];
|
||||
}
|
||||
|
||||
static void DecompressGlyph_Normal(u16 glyphId, bool32 isJapanese)
|
||||
{
|
||||
const u16* glyphs;
|
||||
|
||||
if (isJapanese == TRUE)
|
||||
{
|
||||
glyphs = gFontNormalJapaneseGlyphs + (0x100 * (glyphId >> 0x4)) + (0x8 * (glyphId % 0x10));
|
||||
DecompressGlyphTile(glyphs, gCurGlyph.gfxBufferTop);
|
||||
DecompressGlyphTile(glyphs + 0x80, gCurGlyph.gfxBufferBottom);
|
||||
gCurGlyph.width = 8;
|
||||
gCurGlyph.height = 15;
|
||||
}
|
||||
else
|
||||
{
|
||||
glyphs = gFontNormalLatinGlyphs + (0x20 * glyphId);
|
||||
gCurGlyph.width = gFontNormalLatinGlyphWidths[glyphId];
|
||||
|
||||
if (gCurGlyph.width <= 8)
|
||||
{
|
||||
DecompressGlyphTile(glyphs, gCurGlyph.gfxBufferTop);
|
||||
DecompressGlyphTile(glyphs + 0x10, gCurGlyph.gfxBufferBottom);
|
||||
}
|
||||
else
|
||||
{
|
||||
DecompressGlyphTile(glyphs, gCurGlyph.gfxBufferTop);
|
||||
DecompressGlyphTile(glyphs + 0x8, gCurGlyph.gfxBufferTop + 8);
|
||||
DecompressGlyphTile(glyphs + 0x10, gCurGlyph.gfxBufferBottom);
|
||||
DecompressGlyphTile(glyphs + 0x18, gCurGlyph.gfxBufferBottom + 8);
|
||||
}
|
||||
|
||||
gCurGlyph.height = 15;
|
||||
}
|
||||
}
|
||||
|
||||
static u32 GetGlyphWidth_Normal(u16 glyphId, bool32 isJapanese)
|
||||
{
|
||||
if (isJapanese == TRUE)
|
||||
return 8;
|
||||
else
|
||||
return gFontNormalLatinGlyphWidths[glyphId];
|
||||
}
|
||||
|
||||
static void DecompressGlyph_Bold(u16 glyphId)
|
||||
{
|
||||
const u16* glyphs;
|
||||
|
||||
glyphs = sFontBoldJapaneseGlyphs + (0x100 * (glyphId >> 4)) + (0x8 * (glyphId & 0xF));
|
||||
DecompressGlyphTile(glyphs, gCurGlyph.gfxBufferTop);
|
||||
DecompressGlyphTile(glyphs + 0x80, gCurGlyph.gfxBufferBottom);
|
||||
gCurGlyph.width = 8;
|
||||
gCurGlyph.height = 12;
|
||||
}
|
174
gflib/text.h
Normal file
174
gflib/text.h
Normal file
@ -0,0 +1,174 @@
|
||||
#ifndef GUARD_TEXT_H
|
||||
#define GUARD_TEXT_H
|
||||
|
||||
#include "characters.h"
|
||||
|
||||
#define NUM_TEXT_PRINTERS 32
|
||||
|
||||
// Given as a text speed when all the text should be
|
||||
// loaded at once but not copied to vram yet.
|
||||
#define TEXT_SKIP_DRAW 0xFF
|
||||
|
||||
enum {
|
||||
FONT_SMALL,
|
||||
FONT_NORMAL,
|
||||
FONT_SHORT,
|
||||
FONT_SHORT_COPY_1,
|
||||
FONT_SHORT_COPY_2,
|
||||
FONT_SHORT_COPY_3,
|
||||
FONT_BRAILLE,
|
||||
FONT_NARROW,
|
||||
FONT_SMALL_NARROW, // Very similar to FONT_SMALL, some glyphs are narrower
|
||||
FONT_BOLD, // JP glyph set only
|
||||
};
|
||||
|
||||
// Return values for font functions
|
||||
enum {
|
||||
RENDER_PRINT,
|
||||
RENDER_FINISH,
|
||||
RENDER_REPEAT, // Run render function again, if e.g. a control code is encountered.
|
||||
RENDER_UPDATE,
|
||||
};
|
||||
|
||||
// Text printer states read by RenderText / FontFunc_Braille
|
||||
enum {
|
||||
RENDER_STATE_HANDLE_CHAR,
|
||||
RENDER_STATE_WAIT,
|
||||
RENDER_STATE_CLEAR,
|
||||
RENDER_STATE_SCROLL_START,
|
||||
RENDER_STATE_SCROLL,
|
||||
RENDER_STATE_WAIT_SE,
|
||||
RENDER_STATE_PAUSE,
|
||||
};
|
||||
|
||||
enum {
|
||||
FONTATTR_MAX_LETTER_WIDTH,
|
||||
FONTATTR_MAX_LETTER_HEIGHT,
|
||||
FONTATTR_LETTER_SPACING,
|
||||
FONTATTR_LINE_SPACING,
|
||||
FONTATTR_UNKNOWN, // dunno what this is yet
|
||||
FONTATTR_COLOR_FOREGROUND,
|
||||
FONTATTR_COLOR_BACKGROUND,
|
||||
FONTATTR_COLOR_SHADOW
|
||||
};
|
||||
|
||||
struct TextPrinterSubStruct
|
||||
{
|
||||
u8 fontId:4; // 0x14
|
||||
bool8 hasPrintBeenSpedUp:1;
|
||||
u8 unk:3;
|
||||
u8 downArrowDelay:5;
|
||||
u8 downArrowYPosIdx:2;
|
||||
bool8 hasFontIdBeenSet:1;
|
||||
u8 autoScrollDelay;
|
||||
};
|
||||
|
||||
struct TextPrinterTemplate
|
||||
{
|
||||
const u8* currentChar;
|
||||
u8 windowId;
|
||||
u8 fontId;
|
||||
u8 x;
|
||||
u8 y;
|
||||
u8 currentX; // 0x8
|
||||
u8 currentY;
|
||||
u8 letterSpacing;
|
||||
u8 lineSpacing;
|
||||
u8 unk:4; // 0xC
|
||||
u8 fgColor:4;
|
||||
u8 bgColor:4;
|
||||
u8 shadowColor:4;
|
||||
};
|
||||
|
||||
struct TextPrinter
|
||||
{
|
||||
struct TextPrinterTemplate printerTemplate;
|
||||
|
||||
void (*callback)(struct TextPrinterTemplate *, u16); // 0x10
|
||||
|
||||
u8 subStructFields[7]; // always cast to struct TextPrinterSubStruct... so why bother
|
||||
u8 active;
|
||||
u8 state; // 0x1C
|
||||
u8 textSpeed;
|
||||
u8 delayCounter;
|
||||
u8 scrollDistance;
|
||||
u8 minLetterSpacing; // 0x20
|
||||
u8 japanese;
|
||||
};
|
||||
|
||||
struct FontInfo
|
||||
{
|
||||
u16 (*fontFunction)(struct TextPrinter *x);
|
||||
u8 maxLetterWidth;
|
||||
u8 maxLetterHeight;
|
||||
u8 letterSpacing;
|
||||
u8 lineSpacing;
|
||||
u8 unk:4;
|
||||
u8 fgColor:4;
|
||||
u8 bgColor:4;
|
||||
u8 shadowColor:4;
|
||||
};
|
||||
|
||||
extern const struct FontInfo *gFonts;
|
||||
|
||||
struct GlyphWidthFunc
|
||||
{
|
||||
u32 fontId;
|
||||
u32 (*func)(u16 glyphId, bool32 isJapanese);
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
bool8 canABSpeedUpPrint:1;
|
||||
bool8 useAlternateDownArrow:1;
|
||||
bool8 autoScroll:1;
|
||||
bool8 forceMidTextSpeed:1;
|
||||
} TextFlags;
|
||||
|
||||
struct TextGlyph
|
||||
{
|
||||
u32 gfxBufferTop[16];
|
||||
u32 gfxBufferBottom[16];
|
||||
u8 width;
|
||||
u8 height;
|
||||
};
|
||||
|
||||
extern TextFlags gTextFlags;
|
||||
|
||||
extern u8 gDisableTextPrinters;
|
||||
extern struct TextGlyph gCurGlyph;
|
||||
|
||||
void DeactivateAllTextPrinters(void);
|
||||
u16 AddTextPrinterParameterized(u8 windowId, u8 fontId, const u8 *str, u8 x, u8 y, u8 speed, void (*callback)(struct TextPrinterTemplate *, u16));
|
||||
bool16 AddTextPrinter(struct TextPrinterTemplate *template, u8 speed, void (*callback)(struct TextPrinterTemplate *, u16));
|
||||
void RunTextPrinters(void);
|
||||
bool16 IsTextPrinterActive(u8 id);
|
||||
void GenerateFontHalfRowLookupTable(u8 fgColor, u8 bgColor, u8 shadowColor);
|
||||
void SaveTextColors(u8 *fgColor, u8 *bgColor, u8 *shadowColor);
|
||||
void RestoreTextColors(u8 *fgColor, u8 *bgColor, u8 *shadowColor);
|
||||
void DecompressGlyphTile(const void *src_, void *dest_);
|
||||
void CopyGlyphToWindow(struct TextPrinter *x);
|
||||
void ClearTextSpan(struct TextPrinter *textPrinter, u32 width);
|
||||
u8 GetMenuCursorDimensionByFont(u8, u8);
|
||||
|
||||
void TextPrinterInitDownArrowCounters(struct TextPrinter *textPrinter);
|
||||
void TextPrinterDrawDownArrow(struct TextPrinter *textPrinter);
|
||||
void TextPrinterClearDownArrow(struct TextPrinter *textPrinter);
|
||||
bool8 TextPrinterWaitAutoMode(struct TextPrinter *textPrinter);
|
||||
bool16 TextPrinterWaitWithDownArrow(struct TextPrinter *textPrinter);
|
||||
bool16 TextPrinterWait(struct TextPrinter *textPrinter);
|
||||
void DrawDownArrow(u8 windowId, u16 x, u16 y, u8 bgColor, bool8 drawArrow, u8 *counter, u8 *yCoordIndex);
|
||||
s32 GetStringWidth(u8 fontId, const u8 *str, s16 letterSpacing);
|
||||
u8 RenderTextHandleBold(u8 *pixels, u8 fontId, u8 *str);
|
||||
u8 DrawKeypadIcon(u8 windowId, u8 keypadIconId, u16 x, u16 y);
|
||||
u8 GetKeypadIconTileOffset(u8 keypadIconId);
|
||||
u8 GetKeypadIconWidth(u8 keypadIconId);
|
||||
u8 GetKeypadIconHeight(u8 keypadIconId);
|
||||
void SetDefaultFontsPointer(void);
|
||||
u8 GetFontAttribute(u8 fontId, u8 attributeId);
|
||||
u8 GetMenuCursorDimensionByFont(u8 fontId, u8 whichDimension);
|
||||
|
||||
// braille.c
|
||||
u16 FontFunc_Braille(struct TextPrinter *textPrinter);
|
||||
u32 GetGlyphWidth_Braille(u16 glyphId, bool32 isJapanese);
|
||||
|
||||
#endif // GUARD_TEXT_H
|
719
gflib/window.c
Normal file
719
gflib/window.c
Normal file
@ -0,0 +1,719 @@
|
||||
#include "global.h"
|
||||
#include "window.h"
|
||||
#include "malloc.h"
|
||||
#include "bg.h"
|
||||
#include "blit.h"
|
||||
|
||||
u32 gUnusedWindowVar1;
|
||||
u32 gUnusedWindowVar2;
|
||||
// This global is set to 0 and never changed.
|
||||
u8 gTransparentTileNumber;
|
||||
u32 gUnusedWindowVar3;
|
||||
void *gWindowBgTilemapBuffers[NUM_BACKGROUNDS];
|
||||
extern u32 gWindowTileAutoAllocEnabled;
|
||||
|
||||
#define WINDOWS_MAX 32
|
||||
|
||||
EWRAM_DATA struct Window gWindows[WINDOWS_MAX] = {0};
|
||||
EWRAM_DATA static struct Window* sWindowPtr = NULL;
|
||||
EWRAM_DATA static u16 sWindowSize = 0;
|
||||
|
||||
static u8 GetNumActiveWindowsOnBg(u8 bgId);
|
||||
static u8 GetNumActiveWindowsOnBg8Bit(u8 bgId);
|
||||
|
||||
static const struct WindowTemplate sDummyWindowTemplate = DUMMY_WIN_TEMPLATE;
|
||||
|
||||
static void DummyWindowBgTilemap(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool16 InitWindows(const struct WindowTemplate *templates)
|
||||
{
|
||||
int i;
|
||||
void *bgTilemapBuffer;
|
||||
int j;
|
||||
u8 bgLayer;
|
||||
u16 attrib;
|
||||
u8* allocatedTilemapBuffer;
|
||||
int allocatedBaseBlock;
|
||||
|
||||
for (i = 0; i < NUM_BACKGROUNDS; ++i)
|
||||
{
|
||||
bgTilemapBuffer = GetBgTilemapBuffer(i);
|
||||
if (bgTilemapBuffer != NULL)
|
||||
gWindowBgTilemapBuffers[i] = DummyWindowBgTilemap;
|
||||
else
|
||||
gWindowBgTilemapBuffers[i] = bgTilemapBuffer;
|
||||
}
|
||||
|
||||
for (i = 0; i < WINDOWS_MAX; ++i)
|
||||
{
|
||||
gWindows[i].window = sDummyWindowTemplate;
|
||||
gWindows[i].tileData = NULL;
|
||||
}
|
||||
|
||||
for (i = 0, allocatedBaseBlock = 0, bgLayer = templates[i].bg; bgLayer != 0xFF && i < WINDOWS_MAX; ++i, bgLayer = templates[i].bg)
|
||||
{
|
||||
if (gWindowTileAutoAllocEnabled == TRUE)
|
||||
{
|
||||
allocatedBaseBlock = BgTileAllocOp(bgLayer, 0, templates[i].width * templates[i].height, 0);
|
||||
if (allocatedBaseBlock == -1)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (gWindowBgTilemapBuffers[bgLayer] == NULL)
|
||||
{
|
||||
attrib = GetBgAttribute(bgLayer, BG_ATTR_METRIC);
|
||||
|
||||
if (attrib != 0xFFFF)
|
||||
{
|
||||
allocatedTilemapBuffer = AllocZeroed(attrib);
|
||||
|
||||
if (allocatedTilemapBuffer == NULL)
|
||||
{
|
||||
FreeAllWindowBuffers();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
for (j = 0; j < attrib; ++j)
|
||||
allocatedTilemapBuffer[j] = 0;
|
||||
|
||||
gWindowBgTilemapBuffers[bgLayer] = allocatedTilemapBuffer;
|
||||
SetBgTilemapBuffer(bgLayer, allocatedTilemapBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
allocatedTilemapBuffer = AllocZeroed((u16)(32 * (templates[i].width * templates[i].height)));
|
||||
|
||||
if (allocatedTilemapBuffer == NULL)
|
||||
{
|
||||
if ((GetNumActiveWindowsOnBg(bgLayer) == 0) && (gWindowBgTilemapBuffers[bgLayer] != DummyWindowBgTilemap))
|
||||
{
|
||||
Free(gWindowBgTilemapBuffers[bgLayer]);
|
||||
gWindowBgTilemapBuffers[bgLayer] = allocatedTilemapBuffer;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gWindows[i].tileData = allocatedTilemapBuffer;
|
||||
gWindows[i].window = templates[i];
|
||||
|
||||
if (gWindowTileAutoAllocEnabled == TRUE)
|
||||
{
|
||||
gWindows[i].window.baseBlock = allocatedBaseBlock;
|
||||
BgTileAllocOp(bgLayer, allocatedBaseBlock, templates[i].width * templates[i].height, 1);
|
||||
}
|
||||
}
|
||||
|
||||
gTransparentTileNumber = 0;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
u16 AddWindow(const struct WindowTemplate *template)
|
||||
{
|
||||
u16 win;
|
||||
u8 bgLayer;
|
||||
int allocatedBaseBlock;
|
||||
u16 attrib;
|
||||
u8 *allocatedTilemapBuffer;
|
||||
int i;
|
||||
|
||||
for (win = 0; win < WINDOWS_MAX; ++win)
|
||||
{
|
||||
if ((bgLayer = gWindows[win].window.bg) == 0xFF)
|
||||
break;
|
||||
}
|
||||
|
||||
if (win == WINDOWS_MAX)
|
||||
return WINDOW_NONE;
|
||||
|
||||
bgLayer = template->bg;
|
||||
allocatedBaseBlock = 0;
|
||||
|
||||
if (gWindowTileAutoAllocEnabled == TRUE)
|
||||
{
|
||||
allocatedBaseBlock = BgTileAllocOp(bgLayer, 0, template->width * template->height, 0);
|
||||
|
||||
if (allocatedBaseBlock == -1)
|
||||
return WINDOW_NONE;
|
||||
}
|
||||
|
||||
if (gWindowBgTilemapBuffers[bgLayer] == NULL)
|
||||
{
|
||||
attrib = GetBgAttribute(bgLayer, BG_ATTR_METRIC);
|
||||
|
||||
if (attrib != 0xFFFF)
|
||||
{
|
||||
allocatedTilemapBuffer = AllocZeroed(attrib);
|
||||
|
||||
if (allocatedTilemapBuffer == NULL)
|
||||
return WINDOW_NONE;
|
||||
|
||||
for (i = 0; i < attrib; ++i)
|
||||
allocatedTilemapBuffer[i] = 0;
|
||||
|
||||
gWindowBgTilemapBuffers[bgLayer] = allocatedTilemapBuffer;
|
||||
SetBgTilemapBuffer(bgLayer, allocatedTilemapBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
allocatedTilemapBuffer = AllocZeroed((u16)(32 * (template->width * template->height)));
|
||||
|
||||
if (allocatedTilemapBuffer == NULL)
|
||||
{
|
||||
if ((GetNumActiveWindowsOnBg(bgLayer) == 0) && (gWindowBgTilemapBuffers[bgLayer] != DummyWindowBgTilemap))
|
||||
{
|
||||
Free(gWindowBgTilemapBuffers[bgLayer]);
|
||||
gWindowBgTilemapBuffers[bgLayer] = allocatedTilemapBuffer;
|
||||
}
|
||||
return WINDOW_NONE;
|
||||
}
|
||||
|
||||
gWindows[win].tileData = allocatedTilemapBuffer;
|
||||
gWindows[win].window = *template;
|
||||
|
||||
if (gWindowTileAutoAllocEnabled == TRUE)
|
||||
{
|
||||
gWindows[win].window.baseBlock = allocatedBaseBlock;
|
||||
BgTileAllocOp(bgLayer, allocatedBaseBlock, gWindows[win].window.width * gWindows[win].window.height, 1);
|
||||
}
|
||||
|
||||
return win;
|
||||
}
|
||||
|
||||
int AddWindowWithoutTileMap(const struct WindowTemplate *template)
|
||||
{
|
||||
u16 win;
|
||||
u8 bgLayer;
|
||||
int allocatedBaseBlock;
|
||||
|
||||
for (win = 0; win < WINDOWS_MAX; ++win)
|
||||
{
|
||||
if (gWindows[win].window.bg == 0xFF)
|
||||
break;
|
||||
}
|
||||
|
||||
if (win == WINDOWS_MAX)
|
||||
return WINDOW_NONE;
|
||||
|
||||
bgLayer = template->bg;
|
||||
allocatedBaseBlock = 0;
|
||||
|
||||
if (gWindowTileAutoAllocEnabled == TRUE)
|
||||
{
|
||||
allocatedBaseBlock = BgTileAllocOp(bgLayer, 0, template->width * template->height, 0);
|
||||
|
||||
if (allocatedBaseBlock == -1)
|
||||
return WINDOW_NONE;
|
||||
}
|
||||
|
||||
gWindows[win].window = *template;
|
||||
|
||||
if (gWindowTileAutoAllocEnabled == TRUE)
|
||||
{
|
||||
gWindows[win].window.baseBlock = allocatedBaseBlock;
|
||||
BgTileAllocOp(bgLayer, allocatedBaseBlock, gWindows[win].window.width * gWindows[win].window.height, 1);
|
||||
}
|
||||
|
||||
return win;
|
||||
}
|
||||
|
||||
void RemoveWindow(u8 windowId)
|
||||
{
|
||||
u8 bgLayer = gWindows[windowId].window.bg;
|
||||
|
||||
if (gWindowTileAutoAllocEnabled == TRUE)
|
||||
BgTileAllocOp(bgLayer, gWindows[windowId].window.baseBlock, gWindows[windowId].window.width * gWindows[windowId].window.height, 2);
|
||||
|
||||
gWindows[windowId].window = sDummyWindowTemplate;
|
||||
|
||||
if (GetNumActiveWindowsOnBg(bgLayer) == 0)
|
||||
{
|
||||
if (gWindowBgTilemapBuffers[bgLayer] != DummyWindowBgTilemap)
|
||||
{
|
||||
Free(gWindowBgTilemapBuffers[bgLayer]);
|
||||
gWindowBgTilemapBuffers[bgLayer] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (gWindows[windowId].tileData != NULL)
|
||||
{
|
||||
Free(gWindows[windowId].tileData);
|
||||
gWindows[windowId].tileData = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void FreeAllWindowBuffers(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_BACKGROUNDS; ++i)
|
||||
{
|
||||
if (gWindowBgTilemapBuffers[i] != NULL && gWindowBgTilemapBuffers[i] != DummyWindowBgTilemap)
|
||||
{
|
||||
Free(gWindowBgTilemapBuffers[i]);
|
||||
gWindowBgTilemapBuffers[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < WINDOWS_MAX; ++i)
|
||||
{
|
||||
if (gWindows[i].tileData != NULL)
|
||||
{
|
||||
Free(gWindows[i].tileData);
|
||||
gWindows[i].tileData = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CopyWindowToVram(u8 windowId, u8 mode)
|
||||
{
|
||||
struct Window windowLocal = gWindows[windowId];
|
||||
u16 windowSize = 32 * (windowLocal.window.width * windowLocal.window.height);
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case COPYWIN_MAP:
|
||||
CopyBgTilemapBufferToVram(windowLocal.window.bg);
|
||||
break;
|
||||
case COPYWIN_GFX:
|
||||
LoadBgTiles(windowLocal.window.bg, windowLocal.tileData, windowSize, windowLocal.window.baseBlock);
|
||||
break;
|
||||
case COPYWIN_FULL:
|
||||
LoadBgTiles(windowLocal.window.bg, windowLocal.tileData, windowSize, windowLocal.window.baseBlock);
|
||||
CopyBgTilemapBufferToVram(windowLocal.window.bg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CopyWindowRectToVram(u32 windowId, u32 mode, u32 x, u32 y, u32 w, u32 h)
|
||||
{
|
||||
struct Window windowLocal;
|
||||
int rectSize;
|
||||
int rectPos;
|
||||
|
||||
if (w != 0 && h != 0)
|
||||
{
|
||||
windowLocal = gWindows[windowId];
|
||||
|
||||
rectSize = ((h - 1) * windowLocal.window.width);
|
||||
rectSize += (windowLocal.window.width - x);
|
||||
rectSize -= (windowLocal.window.width - (x + w));
|
||||
rectSize *= 32;
|
||||
|
||||
rectPos = (y * windowLocal.window.width) + x;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case COPYWIN_MAP:
|
||||
CopyBgTilemapBufferToVram(windowLocal.window.bg);
|
||||
break;
|
||||
case COPYWIN_GFX:
|
||||
LoadBgTiles(windowLocal.window.bg, windowLocal.tileData + (rectPos * 32), rectSize, windowLocal.window.baseBlock + rectPos);
|
||||
break;
|
||||
case COPYWIN_FULL:
|
||||
LoadBgTiles(windowLocal.window.bg, windowLocal.tileData + (rectPos * 32), rectSize, windowLocal.window.baseBlock + rectPos);
|
||||
CopyBgTilemapBufferToVram(windowLocal.window.bg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PutWindowTilemap(u8 windowId)
|
||||
{
|
||||
struct Window windowLocal = gWindows[windowId];
|
||||
|
||||
WriteSequenceToBgTilemapBuffer(
|
||||
windowLocal.window.bg,
|
||||
GetBgAttribute(windowLocal.window.bg, BG_ATTR_BASETILE) + windowLocal.window.baseBlock,
|
||||
windowLocal.window.tilemapLeft,
|
||||
windowLocal.window.tilemapTop,
|
||||
windowLocal.window.width,
|
||||
windowLocal.window.height,
|
||||
windowLocal.window.paletteNum,
|
||||
1);
|
||||
}
|
||||
|
||||
void PutWindowRectTilemapOverridePalette(u8 windowId, u8 x, u8 y, u8 width, u8 height, u8 palette)
|
||||
{
|
||||
struct Window windowLocal = gWindows[windowId];
|
||||
u16 currentRow = windowLocal.window.baseBlock + (y * windowLocal.window.width) + x + GetBgAttribute(windowLocal.window.bg, BG_ATTR_BASETILE);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < height; ++i)
|
||||
{
|
||||
WriteSequenceToBgTilemapBuffer(
|
||||
windowLocal.window.bg,
|
||||
currentRow,
|
||||
windowLocal.window.tilemapLeft + x,
|
||||
windowLocal.window.tilemapTop + y + i,
|
||||
width,
|
||||
1,
|
||||
palette,
|
||||
1);
|
||||
|
||||
currentRow += windowLocal.window.width;
|
||||
}
|
||||
}
|
||||
|
||||
// Fills a window with transparent tiles.
|
||||
void ClearWindowTilemap(u8 windowId)
|
||||
{
|
||||
struct Window windowLocal = gWindows[windowId];
|
||||
|
||||
FillBgTilemapBufferRect(
|
||||
windowLocal.window.bg,
|
||||
gTransparentTileNumber,
|
||||
windowLocal.window.tilemapLeft,
|
||||
windowLocal.window.tilemapTop,
|
||||
windowLocal.window.width,
|
||||
windowLocal.window.height,
|
||||
windowLocal.window.paletteNum);
|
||||
}
|
||||
|
||||
void PutWindowRectTilemap(u8 windowId, u8 x, u8 y, u8 width, u8 height)
|
||||
{
|
||||
struct Window windowLocal = gWindows[windowId];
|
||||
u16 currentRow = windowLocal.window.baseBlock + (y * windowLocal.window.width) + x + GetBgAttribute(windowLocal.window.bg, BG_ATTR_BASETILE);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < height; ++i)
|
||||
{
|
||||
WriteSequenceToBgTilemapBuffer(
|
||||
windowLocal.window.bg,
|
||||
currentRow,
|
||||
windowLocal.window.tilemapLeft + x,
|
||||
windowLocal.window.tilemapTop + y + i,
|
||||
width,
|
||||
1,
|
||||
windowLocal.window.paletteNum,
|
||||
1);
|
||||
|
||||
currentRow += windowLocal.window.width;
|
||||
}
|
||||
}
|
||||
|
||||
void BlitBitmapToWindow(u8 windowId, const u8 *pixels, u16 x, u16 y, u16 width, u16 height)
|
||||
{
|
||||
BlitBitmapRectToWindow(windowId, pixels, 0, 0, width, height, x, y, width, height);
|
||||
}
|
||||
|
||||
void BlitBitmapRectToWindow(u8 windowId, const u8 *pixels, u16 srcX, u16 srcY, u16 srcWidth, int srcHeight, u16 destX, u16 destY, u16 rectWidth, u16 rectHeight)
|
||||
{
|
||||
struct Bitmap sourceRect;
|
||||
struct Bitmap destRect;
|
||||
|
||||
sourceRect.pixels = (u8*)pixels;
|
||||
sourceRect.width = srcWidth;
|
||||
sourceRect.height = srcHeight;
|
||||
|
||||
destRect.pixels = gWindows[windowId].tileData;
|
||||
destRect.width = 8 * gWindows[windowId].window.width;
|
||||
destRect.height = 8 * gWindows[windowId].window.height;
|
||||
|
||||
BlitBitmapRect4Bit(&sourceRect, &destRect, srcX, srcY, destX, destY, rectWidth, rectHeight, 0);
|
||||
}
|
||||
|
||||
static void BlitBitmapRectToWindowWithColorKey(u8 windowId, const u8 *pixels, u16 srcX, u16 srcY, u16 srcWidth, int srcHeight, u16 destX, u16 destY, u16 rectWidth, u16 rectHeight, u8 colorKey)
|
||||
{
|
||||
struct Bitmap sourceRect;
|
||||
struct Bitmap destRect;
|
||||
|
||||
sourceRect.pixels = (u8*)pixels;
|
||||
sourceRect.width = srcWidth;
|
||||
sourceRect.height = srcHeight;
|
||||
|
||||
destRect.pixels = gWindows[windowId].tileData;
|
||||
destRect.width = 8 * gWindows[windowId].window.width;
|
||||
destRect.height = 8 * gWindows[windowId].window.height;
|
||||
|
||||
BlitBitmapRect4Bit(&sourceRect, &destRect, srcX, srcY, destX, destY, rectWidth, rectHeight, colorKey);
|
||||
}
|
||||
|
||||
void FillWindowPixelRect(u8 windowId, u8 fillValue, u16 x, u16 y, u16 width, u16 height)
|
||||
{
|
||||
struct Bitmap pixelRect;
|
||||
|
||||
pixelRect.pixels = gWindows[windowId].tileData;
|
||||
pixelRect.width = 8 * gWindows[windowId].window.width;
|
||||
pixelRect.height = 8 * gWindows[windowId].window.height;
|
||||
|
||||
FillBitmapRect4Bit(&pixelRect, x, y, width, height, fillValue);
|
||||
}
|
||||
|
||||
void CopyToWindowPixelBuffer(u8 windowId, const void *src, u16 size, u16 tileOffset)
|
||||
{
|
||||
if (size != 0)
|
||||
CpuCopy16(src, gWindows[windowId].tileData + (32 * tileOffset), size);
|
||||
else
|
||||
LZ77UnCompWram(src, gWindows[windowId].tileData + (32 * tileOffset));
|
||||
}
|
||||
|
||||
// Sets all pixels within the window to the fillValue color.
|
||||
void FillWindowPixelBuffer(u8 windowId, u8 fillValue)
|
||||
{
|
||||
int fillSize = gWindows[windowId].window.width * gWindows[windowId].window.height;
|
||||
CpuFastFill8(fillValue, gWindows[windowId].tileData, 32 * fillSize);
|
||||
}
|
||||
|
||||
#define MOVE_TILES_DOWN(a) \
|
||||
{ \
|
||||
destOffset = i + (a); \
|
||||
srcOffset = i + (((width * (distanceLoop & ~7)) | (distanceLoop & 7)) * 4); \
|
||||
if (srcOffset < size) \
|
||||
*(u32*)(tileData + destOffset) = *(u32*)(tileData + srcOffset); \
|
||||
else \
|
||||
*(u32*)(tileData + destOffset) = fillValue32; \
|
||||
distanceLoop++; \
|
||||
}
|
||||
|
||||
#define MOVE_TILES_UP(a) \
|
||||
{ \
|
||||
destOffset = i + (a); \
|
||||
srcOffset = i + (((width * (distanceLoop & ~7)) | (distanceLoop & 7)) * 4); \
|
||||
if (srcOffset < size) \
|
||||
*(u32*)(tileData - destOffset) = *(u32*)(tileData - srcOffset); \
|
||||
else \
|
||||
*(u32*)(tileData - destOffset) = fillValue32; \
|
||||
distanceLoop++; \
|
||||
}
|
||||
|
||||
void ScrollWindow(u8 windowId, u8 direction, u8 distance, u8 fillValue)
|
||||
{
|
||||
struct WindowTemplate window = gWindows[windowId].window;
|
||||
u8 *tileData = gWindows[windowId].tileData;
|
||||
u32 fillValue32 = (fillValue << 24) | (fillValue << 16) | (fillValue << 8) | fillValue;
|
||||
s32 size = window.height * window.width * 32;
|
||||
u32 width = window.width;
|
||||
s32 i;
|
||||
s32 srcOffset, destOffset;
|
||||
u32 distanceLoop;
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
case 0:
|
||||
for (i = 0; i < size; i += 32)
|
||||
{
|
||||
distanceLoop = distance;
|
||||
MOVE_TILES_DOWN(0)
|
||||
MOVE_TILES_DOWN(4)
|
||||
MOVE_TILES_DOWN(8)
|
||||
MOVE_TILES_DOWN(12)
|
||||
MOVE_TILES_DOWN(16)
|
||||
MOVE_TILES_DOWN(20)
|
||||
MOVE_TILES_DOWN(24)
|
||||
MOVE_TILES_DOWN(28)
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
tileData += size - 4;
|
||||
for (i = 0; i < size; i += 32)
|
||||
{
|
||||
distanceLoop = distance;
|
||||
MOVE_TILES_UP(0)
|
||||
MOVE_TILES_UP(4)
|
||||
MOVE_TILES_UP(8)
|
||||
MOVE_TILES_UP(12)
|
||||
MOVE_TILES_UP(16)
|
||||
MOVE_TILES_UP(20)
|
||||
MOVE_TILES_UP(24)
|
||||
MOVE_TILES_UP(28)
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CallWindowFunction(u8 windowId, void ( *func)(u8, u8, u8, u8, u8, u8))
|
||||
{
|
||||
struct WindowTemplate window = gWindows[windowId].window;
|
||||
func(window.bg, window.tilemapLeft, window.tilemapTop, window.width, window.height, window.paletteNum);
|
||||
}
|
||||
|
||||
bool8 SetWindowAttribute(u8 windowId, u8 attributeId, u32 value)
|
||||
{
|
||||
switch (attributeId)
|
||||
{
|
||||
case WINDOW_TILEMAP_LEFT:
|
||||
gWindows[windowId].window.tilemapLeft = value;
|
||||
return FALSE;
|
||||
case WINDOW_TILEMAP_TOP:
|
||||
gWindows[windowId].window.tilemapTop = value;
|
||||
return FALSE;
|
||||
case WINDOW_PALETTE_NUM:
|
||||
gWindows[windowId].window.paletteNum = value;
|
||||
return FALSE;
|
||||
case WINDOW_BASE_BLOCK:
|
||||
gWindows[windowId].window.baseBlock = value;
|
||||
return FALSE;
|
||||
case WINDOW_TILE_DATA:
|
||||
gWindows[windowId].tileData = (u8*)(value);
|
||||
return TRUE;
|
||||
case WINDOW_BG:
|
||||
case WINDOW_WIDTH:
|
||||
case WINDOW_HEIGHT:
|
||||
default:
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
u32 GetWindowAttribute(u8 windowId, u8 attributeId)
|
||||
{
|
||||
switch (attributeId)
|
||||
{
|
||||
case WINDOW_BG:
|
||||
return gWindows[windowId].window.bg;
|
||||
case WINDOW_TILEMAP_LEFT:
|
||||
return gWindows[windowId].window.tilemapLeft;
|
||||
case WINDOW_TILEMAP_TOP:
|
||||
return gWindows[windowId].window.tilemapTop;
|
||||
case WINDOW_WIDTH:
|
||||
return gWindows[windowId].window.width;
|
||||
case WINDOW_HEIGHT:
|
||||
return gWindows[windowId].window.height;
|
||||
case WINDOW_PALETTE_NUM:
|
||||
return gWindows[windowId].window.paletteNum;
|
||||
case WINDOW_BASE_BLOCK:
|
||||
return gWindows[windowId].window.baseBlock;
|
||||
case WINDOW_TILE_DATA:
|
||||
return (u32)(gWindows[windowId].tileData);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static u8 GetNumActiveWindowsOnBg(u8 bgId)
|
||||
{
|
||||
u8 windowsNum = 0;
|
||||
s32 i;
|
||||
for (i = 0; i < WINDOWS_MAX; i++)
|
||||
{
|
||||
if (gWindows[i].window.bg == bgId)
|
||||
windowsNum++;
|
||||
}
|
||||
return windowsNum;
|
||||
}
|
||||
|
||||
static void DummyWindowBgTilemap8Bit(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
u16 AddWindow8Bit(const struct WindowTemplate *template)
|
||||
{
|
||||
u16 windowId;
|
||||
u8* memAddress;
|
||||
u8 bgLayer;
|
||||
|
||||
for (windowId = 0; windowId < WINDOWS_MAX; windowId++)
|
||||
{
|
||||
if (gWindows[windowId].window.bg == 0xFF)
|
||||
break;
|
||||
}
|
||||
if (windowId == WINDOWS_MAX)
|
||||
return WINDOW_NONE;
|
||||
bgLayer = template->bg;
|
||||
if (gWindowBgTilemapBuffers[bgLayer] == NULL)
|
||||
{
|
||||
u16 attribute = GetBgAttribute(bgLayer, BG_ATTR_METRIC);
|
||||
if (attribute != 0xFFFF)
|
||||
{
|
||||
s32 i;
|
||||
memAddress = Alloc(attribute);
|
||||
if (memAddress == NULL)
|
||||
return WINDOW_NONE;
|
||||
for (i = 0; i < attribute; i++) // if we're going to zero out the memory anyway, why not call AllocZeroed?
|
||||
memAddress[i] = 0;
|
||||
gWindowBgTilemapBuffers[bgLayer] = memAddress;
|
||||
SetBgTilemapBuffer(bgLayer, memAddress);
|
||||
}
|
||||
}
|
||||
memAddress = Alloc((u16)(64 * (template->width * template->height)));
|
||||
if (memAddress == NULL)
|
||||
{
|
||||
if (GetNumActiveWindowsOnBg8Bit(bgLayer) == 0 && gWindowBgTilemapBuffers[bgLayer] != DummyWindowBgTilemap8Bit)
|
||||
{
|
||||
Free(gWindowBgTilemapBuffers[bgLayer]);
|
||||
gWindowBgTilemapBuffers[bgLayer] = NULL;
|
||||
}
|
||||
return WINDOW_NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
gWindows[windowId].tileData = memAddress;
|
||||
gWindows[windowId].window = *template;
|
||||
return windowId;
|
||||
}
|
||||
}
|
||||
|
||||
void FillWindowPixelBuffer8Bit(u8 windowId, u8 fillValue)
|
||||
{
|
||||
s32 i;
|
||||
s32 size;
|
||||
|
||||
size = (u16)(64 * (gWindows[windowId].window.width * gWindows[windowId].window.height));
|
||||
for (i = 0; i < size; i++)
|
||||
gWindows[windowId].tileData[i] = fillValue;
|
||||
}
|
||||
|
||||
void FillWindowPixelRect8Bit(u8 windowId, u8 fillValue, u16 x, u16 y, u16 width, u16 height)
|
||||
{
|
||||
struct Bitmap pixelRect;
|
||||
|
||||
pixelRect.pixels = gWindows[windowId].tileData;
|
||||
pixelRect.width = 8 * gWindows[windowId].window.width;
|
||||
pixelRect.height = 8 * gWindows[windowId].window.height;
|
||||
|
||||
FillBitmapRect8Bit(&pixelRect, x, y, width, height, fillValue);
|
||||
}
|
||||
|
||||
void BlitBitmapRectToWindow4BitTo8Bit(u8 windowId, const u8 *pixels, u16 srcX, u16 srcY, u16 srcWidth, int srcHeight, u16 destX, u16 destY, u16 rectWidth, u16 rectHeight, u8 paletteNum)
|
||||
{
|
||||
struct Bitmap sourceRect;
|
||||
struct Bitmap destRect;
|
||||
|
||||
sourceRect.pixels = (u8*) pixels;
|
||||
sourceRect.width = srcWidth;
|
||||
sourceRect.height = srcHeight;
|
||||
|
||||
destRect.pixels = gWindows[windowId].tileData;
|
||||
destRect.width = 8 * gWindows[windowId].window.width;
|
||||
destRect.height = 8 * gWindows[windowId].window.height;
|
||||
|
||||
BlitBitmapRect4BitTo8Bit(&sourceRect, &destRect, srcX, srcY, destX, destY, rectWidth, rectHeight, 0, paletteNum);
|
||||
}
|
||||
|
||||
void CopyWindowToVram8Bit(u8 windowId, u8 mode)
|
||||
{
|
||||
sWindowPtr = &gWindows[windowId];
|
||||
sWindowSize = 64 * (sWindowPtr->window.width * sWindowPtr->window.height);
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case COPYWIN_MAP:
|
||||
CopyBgTilemapBufferToVram(sWindowPtr->window.bg);
|
||||
break;
|
||||
case COPYWIN_GFX:
|
||||
LoadBgTiles(sWindowPtr->window.bg, sWindowPtr->tileData, sWindowSize, sWindowPtr->window.baseBlock);
|
||||
break;
|
||||
case COPYWIN_FULL:
|
||||
LoadBgTiles(sWindowPtr->window.bg, sWindowPtr->tileData, sWindowSize, sWindowPtr->window.baseBlock);
|
||||
CopyBgTilemapBufferToVram(sWindowPtr->window.bg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static u8 GetNumActiveWindowsOnBg8Bit(u8 bgId)
|
||||
{
|
||||
u8 windowsNum = 0;
|
||||
s32 i;
|
||||
for (i = 0; i < WINDOWS_MAX; i++)
|
||||
{
|
||||
if (gWindows[i].window.bg == bgId)
|
||||
windowsNum++;
|
||||
}
|
||||
return windowsNum;
|
||||
}
|
81
gflib/window.h
Normal file
81
gflib/window.h
Normal file
@ -0,0 +1,81 @@
|
||||
#ifndef GUARD_WINDOW_H
|
||||
#define GUARD_WINDOW_H
|
||||
|
||||
#define PIXEL_FILL(num) ((num) | ((num) << 4))
|
||||
|
||||
enum {
|
||||
WINDOW_BG,
|
||||
WINDOW_TILEMAP_LEFT,
|
||||
WINDOW_TILEMAP_TOP,
|
||||
WINDOW_WIDTH,
|
||||
WINDOW_HEIGHT,
|
||||
WINDOW_PALETTE_NUM,
|
||||
WINDOW_BASE_BLOCK,
|
||||
WINDOW_TILE_DATA
|
||||
};
|
||||
|
||||
// Mode for CopyWindowToVram, CopyWindowRectToVram and CopyWindowToVram8Bit
|
||||
enum {
|
||||
COPYWIN_NONE,
|
||||
COPYWIN_MAP,
|
||||
COPYWIN_GFX,
|
||||
COPYWIN_FULL,
|
||||
};
|
||||
|
||||
struct WindowTemplate
|
||||
{
|
||||
u8 bg;
|
||||
u8 tilemapLeft;
|
||||
u8 tilemapTop;
|
||||
u8 width;
|
||||
u8 height;
|
||||
u8 paletteNum;
|
||||
u16 baseBlock;
|
||||
};
|
||||
|
||||
#define DUMMY_WIN_TEMPLATE \
|
||||
{ \
|
||||
.bg = 0xFF, \
|
||||
}
|
||||
|
||||
#define WINDOW_NONE 0xFF
|
||||
|
||||
struct Window
|
||||
{
|
||||
struct WindowTemplate window;
|
||||
u8 *tileData;
|
||||
};
|
||||
|
||||
bool16 InitWindows(const struct WindowTemplate *templates);
|
||||
u16 AddWindow(const struct WindowTemplate *template);
|
||||
int AddWindowWithoutTileMap(const struct WindowTemplate *template);
|
||||
void RemoveWindow(u8 windowId);
|
||||
void FreeAllWindowBuffers(void);
|
||||
void CopyWindowToVram(u8 windowId, u8 mode);
|
||||
void CopyWindowRectToVram(u32 windowId, u32 mode, u32 x, u32 y, u32 w, u32 h);
|
||||
void PutWindowTilemap(u8 windowId);
|
||||
void PutWindowRectTilemapOverridePalette(u8 windowId, u8 x, u8 y, u8 width, u8 height, u8 palette);
|
||||
void ClearWindowTilemap(u8 windowId);
|
||||
void PutWindowRectTilemap(u8 windowId, u8 x, u8 y, u8 width, u8 height);
|
||||
void BlitBitmapToWindow(u8 windowId, const u8 *pixels, u16 x, u16 y, u16 width, u16 height);
|
||||
void BlitBitmapRectToWindow(u8 windowId, const u8 *pixels, u16 srcX, u16 srcY, u16 srcWidth, int srcHeight, u16 destX, u16 destY, u16 rectWidth, u16 rectHeight);
|
||||
void FillWindowPixelRect(u8 windowId, u8 fillValue, u16 x, u16 y, u16 width, u16 height);
|
||||
void CopyToWindowPixelBuffer(u8 windowId, const void *src, u16 size, u16 tileOffset);
|
||||
void FillWindowPixelBuffer(u8 windowId, u8 fillValue);
|
||||
void ScrollWindow(u8 windowId, u8 direction, u8 distance, u8 fillValue);
|
||||
void CallWindowFunction(u8 windowId, void ( *func)(u8, u8, u8, u8, u8, u8));
|
||||
bool8 SetWindowAttribute(u8 windowId, u8 attributeId, u32 value);
|
||||
u32 GetWindowAttribute(u8 windowId, u8 attributeId);
|
||||
u16 AddWindow8Bit(const struct WindowTemplate *template);
|
||||
void FillWindowPixelBuffer8Bit(u8 windowId, u8 fillValue);
|
||||
void FillWindowPixelRect8Bit(u8 windowId, u8 fillValue, u16 x, u16 y, u16 width, u16 height);
|
||||
void BlitBitmapRectToWindow4BitTo8Bit(u8 windowId, const u8 *pixels, u16 srcX, u16 srcY, u16 srcWidth, int srcHeight, u16 destX, u16 destY, u16 rectWidth, u16 rectHeight, u8 paletteNum);
|
||||
void CopyWindowToVram8Bit(u8 windowId, u8 mode);
|
||||
|
||||
extern struct Window gWindows[];
|
||||
extern void* gWindowBgTilemapBuffers[];
|
||||
extern u32 gUnusedWindowVar1;
|
||||
extern u32 gUnusedWindowVar2;
|
||||
extern u32 gUnusedWindowVar3;
|
||||
|
||||
#endif // GUARD_WINDOW_H
|
Reference in New Issue
Block a user