diff options
author | Kurausukun <lord.uber1@gmail.com> | 2021-06-17 22:09:48 -0400 |
---|---|---|
committer | Kurausukun <lord.uber1@gmail.com> | 2021-06-17 22:09:48 -0400 |
commit | 8b59909ac5eb6e3540aeb78396943d57a9702e4d (patch) | |
tree | 3ea51f01171cc1bd6444ab4a4b833cf09eb0463c /src | |
parent | 780da6bfd08b5401a7e11f1e0d38e377f2fab3dd (diff) |
remove gflib
Diffstat (limited to 'src')
-rw-r--r-- | src/bg.c | 1248 | ||||
-rw-r--r-- | src/blit.c | 209 | ||||
-rw-r--r-- | src/dma3_manager.c | 183 | ||||
-rw-r--r-- | src/gpu_regs.c | 195 | ||||
-rw-r--r-- | src/io_reg.c | 36 | ||||
-rw-r--r-- | src/malloc.c | 210 | ||||
-rw-r--r-- | src/sprite.c | 1775 | ||||
-rw-r--r-- | src/string_util.c | 781 | ||||
-rw-r--r-- | src/text.c | 1808 | ||||
-rw-r--r-- | src/window.c | 721 |
10 files changed, 7166 insertions, 0 deletions
diff --git a/src/bg.c b/src/bg.c new file mode 100644 index 000000000..ec7c2113b --- /dev/null +++ b/src/bg.c @@ -0,0 +1,1248 @@ +#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 gUnneededFireRedVariable; + +static const struct BgConfig sZeroedBgControlStruct = { 0 }; + +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; +} + +int DummiedOutFireRedLeafGreenTileAllocFunc(int a1, int a2, int a3, int a4) +{ + return 0; +} + +void ResetBgsAndClearDma3BusyFlags(u32 leftoverFireRedLeafGreenVariable) +{ + int i; + ResetBgs(); + + for (i = 0; i < NUM_BACKGROUNDS; i++) + { + sDmaBusyBitfield[i] = 0; + } + + gUnneededFireRedVariable = 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 (gUnneededFireRedVariable == 1) + { + DummiedOutFireRedLeafGreenTileAllocFunc(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 0: + return GetBgMetricTextMode(bg, 0) * 0x800; + case 1: + 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 0: + default: + sGpuBgConfigs2[bg].bg_x = value; + break; + case 1: + sGpuBgConfigs2[bg].bg_x += value; + break; + case 2: + 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 0: + default: + sGpuBgConfigs2[bg].bg_y = value; + break; + case 1: + sGpuBgConfigs2[bg].bg_y += value; + break; + case 2: + 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 0: + default: + sGpuBgConfigs2[bg].bg_y = value; + break; + case 1: + sGpuBgConfigs2[bg].bg_y += value; + break; + case 2: + 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 0: + sizeToLoad = GetBgMetricTextMode(bg, 0) * 0x800; + break; + case 1: + 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 0: + { + 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 1: + { + 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 unused, u8 srcHeight, u8 destX, u8 destY, u8 rectWidth, u8 rectHeight, s16 palette1, s16 tileOffset) +{ + 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 0: + srcPtr = src + ((srcY * srcWidth) + srcX) * 2; + for (i = destX; i < (destX + rectWidth); i++) + { + for (j = srcHeight; j < (srcHeight + destY); j++) + { + u16 index = GetTileMapIndexFromCoords(j, i, screenSize, screenWidth, screenHeight); + CopyTileMapEntry(srcPtr, sGpuBgConfigs2[bg].tilemap + (index * 2), rectHeight, palette1, tileOffset); + srcPtr += 2; + } + srcPtr += (srcWidth - destY) * 2; + } + break; + case 1: + srcPtr = src + ((srcY * srcWidth) + srcX); + var = GetBgMetricAffineMode(bg, 0x1); + for (i = destX; i < (destX + rectWidth); i++) + { + for (j = srcHeight; j < (srcHeight + destY); j++) + { + *(u8*)(sGpuBgConfigs2[bg].tilemap + ((var * i) + j)) = *(u8*)(srcPtr) + palette1; + srcPtr++; + } + srcPtr += (srcWidth - destY); + } + 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 0: + for (y16 = y; y16 < (y + height); y16++) + { + for (x16 = x; x16 < (x + width); x16++) + { + ((u16*)sGpuBgConfigs2[bg].tilemap)[((y16 * 0x20) + x16)] = tileNum; + } + } + break; + case 1: + 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 0: + 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 1: + 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; +} + +u32 GetBgType(u8 bg) +{ + u8 mode = GetBgMode(); + + switch (bg) + { + case 0: + case 1: + switch (mode) + { + case 0: + case 1: + return 0; + } + break; + case 2: + switch (mode) + { + case 0: + return 0; + case 1: + case 2: + return 1; + } + break; + case 3: + switch (mode) + { + case 0: + return 0; + case 2: + return 1; + } + break; + } + + return 0xFFFF; +} + +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; +} diff --git a/src/blit.c b/src/blit.c new file mode 100644 index 000000000..bdbb2e2fd --- /dev/null +++ b/src/blit.c @@ -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; + } + } +} diff --git a/src/dma3_manager.c b/src/dma3_manager.c new file mode 100644 index 000000000..d774efe8c --- /dev/null +++ b/src/dma3_manager.c @@ -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; + } +} diff --git a/src/gpu_regs.c b/src/gpu_regs.c new file mode 100644 index 000000000..3bcc4fd93 --- /dev/null +++ b/src/gpu_regs.c @@ -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); +} diff --git a/src/io_reg.c b/src/io_reg.c new file mode 100644 index 000000000..44364349d --- /dev/null +++ b/src/io_reg.c @@ -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, +}; diff --git a/src/malloc.c b/src/malloc.c new file mode 100644 index 000000000..38fc8939e --- /dev/null +++ b/src/malloc.c @@ -0,0 +1,210 @@ +#include "global.h" + +static void *sHeapStart; +static u32 sHeapSize; +static u32 sFiller; // needed to align dma3_manager.o(.bss) + +#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; +} diff --git a/src/sprite.c b/src/sprite.c new file mode 100644 index 000000000..f97ecc712 --- /dev/null +++ b/src/sprite.c @@ -0,0 +1,1775 @@ +#include "global.h" +#include "sprite.h" +#include "main.h" +#include "palette.h" + +#define MAX_SPRITE_COPY_REQUESTS 64 + +#define OAM_MATRIX_COUNT 32 + +#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 obj_update_pos2(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 = 160, \ + .affineMode = 0, \ + .objMode = 0, \ + .mosaic = 0, \ + .bpp = 0, \ + .shape = SPRITE_SHAPE(8x8), \ + .x = 304, \ + .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, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .template = &gDummySpriteTemplate, + .subspriteTables = NULL, + .callback = SpriteCallbackDummy, + .pos1 = { 304, 160 }, + .pos2 = { 0, 0 }, + .centerToCornerVecX = 0, + .centerToCornerVecY = 0, + .animNum = 0, + .animCmdIndex = 0, + .animDelayCounter = 0, + .animPaused = 0, + .affineAnimPaused = 0, + .animLoopCounter = 0, + .data = {0, 0, 0, 0, 0, 0, 0}, + .inUse = 0, + .coordOffsetEnabled = 0, + .invisible = FALSE, + .flags_3 = 0, + .flags_4 = 0, + .flags_5 = 0, + .flags_6 = 0, + .flags_7 = 0, + .hFlip = 0, + .vFlip = 0, + .animBeginning = 0, + .affineAnimBeginning = 0, + .animEnded = 0, + .affineAnimEnded = 0, + .usingSheet = 0, + .flags_f = 0, + .sheetTileStart = 0, + .subspriteTableNum = 0, + .subspriteMode = 0, + .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 = 0xFFFF, + .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->pos1.x + sprite->pos2.x + sprite->centerToCornerVecX + gSpriteCoordOffsetX; + sprite->oam.y = sprite->pos1.y + sprite->pos2.y + sprite->centerToCornerVecY + gSpriteCoordOffsetY; + } + else + { + sprite->oam.x = sprite->pos1.x + sprite->pos2.x + sprite->centerToCornerVecX; + sprite->oam.y = sprite->pos1.y + sprite->pos2.y + 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->pos1.x = x; + sprite->pos1.y = y; + + CalcCenterToCornerVec(sprite, sprite->oam.shape, sprite->oam.size, sprite->oam.affineMode); + + if (template->tileTag == 0xFFFF) + { + 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 != 0xFFFF) + 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]); +} + +// UB: template pointer may point to freed temporary storage +void FreeSpriteTiles(struct Sprite *sprite) +{ + if (sprite->template->tileTag != 0xFFFF) + FreeSpriteTilesByTag(sprite->template->tileTag); +} + +// UB: template pointer may point to freed temporary storage +void FreeSpritePalette(struct Sprite *sprite) +{ + 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->flags_f) + obj_update_pos2(sprite, sprite->data[6], sprite->data[7]); + } +} + +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->flags_f) + obj_update_pos2(sprite, sprite->data[6], sprite->data[7]); + } +} + +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; +} + +void sub_8007E18(struct Sprite* sprite, s16 a2, s16 a3) +{ + sprite->data[6] = a2; + sprite->data[7] = a3; + sprite->flags_f = 1; +} + +s32 sub_8007E28(s32 a0, s32 a1, s32 a2) +{ + s32 subResult, var1; + + subResult = a1 - a0; + if (subResult < 0) + var1 = -(subResult) >> 9; + else + var1 = -(subResult >> 9); + return a2 - ((u32)(a2 * a1) / (u32)(a0) + var1); +} + +void obj_update_pos2(struct Sprite *sprite, s32 a1, s32 a2) +{ + s32 var0, var1, var2; + + u32 matrixNum = sprite->oam.matrixNum; + if (a1 != 0x800) + { + var0 = sOamDimensions32[sprite->oam.shape][sprite->oam.size].width; + var1 = var0 << 8; + var2 = (var0 << 16) / gOamMatrices[matrixNum].a; + sprite->pos2.x = sub_8007E28(var1, var2, a1); + } + if (a2 != 0x800) + { + var0 = sOamDimensions32[sprite->oam.shape][sprite->oam.size].height; + var1 = var0 << 8; + var2 = (var0 << 16) / gOamMatrices[matrixNum].d; + sprite->pos2.y = sub_8007E28(var1, var2, a2); + } +} + +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] = 0xFFFF; + } +} + +void FreeSpriteTileRanges(void) +{ + u8 i; + + for (i = 0; i < MAX_SPRITES; i++) + { + sSpriteTileRangeTags[i] = 0xFFFF; + 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] != 0xFFFF && sSpriteTileRanges[i * 2] == start) + return sSpriteTileRangeTags[i]; + } + + return 0xFFFF; +} + +void AllocSpriteTileRange(u16 tag, u16 start, u16 count) +{ + u8 freeIndex = IndexOfSpriteTileTag(0xFFFF); + 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] = 0xFFFF; +} + +u8 LoadSpritePalette(const struct SpritePalette *palette) +{ + u8 index = IndexOfSpritePaletteTag(palette->tag); + + if (index != 0xFF) + return index; + + index = IndexOfSpritePaletteTag(0xFFFF); + + 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(0xFFFF); + 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] = 0xFFFF; +} + +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; +} diff --git a/src/string_util.c b/src/string_util.c new file mode 100644 index 000000000..92f8eea5a --- /dev/null +++ b/src/string_util.c @@ -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 *StringCopy10(u8 *dest, const u8 *src) +{ + u8 i; + u32 limit = 10; + + for (i = 0; i < limit; i++) + { + dest[i] = src[i]; + + if (dest[i] == EOS) + return &dest[i]; + } + + dest[i] = EOS; + return &dest[i]; +} + +u8 *StringGetEnd10(u8 *str) +{ + u8 i; + u32 limit = 10; + + for (i = 0; i < limit; i++) + if (str[i] == EOS) + return &str[i]; + + str[i] = EOS; + return &str[i]; +} + +u8 *StringCopy7(u8 *dest, const u8 *src) +{ + s32 i; + s32 limit = 7; + + 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++ = 0x77; + } + + 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++ = 0x77; + } + + 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++ = 0x77; + } + + 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_SIZE, + 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 + 0x40; + 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 < CHAR_0) + if (*str != CHAR_SPACE) + return TRUE; + str++; + } + + return FALSE; +} + +bool32 sub_800924C(u8 *str, s32 n) +{ + s32 i; + + for (i = 0; *str != EOS && i < n; i++) + { + if (*str < CHAR_0) + 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_SIZE] = 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; +} diff --git a/src/text.c b/src/text.c new file mode 100644 index 000000000..eb993c421 --- /dev/null +++ b/src/text.c @@ -0,0 +1,1808 @@ +#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" + +EWRAM_DATA struct TextPrinter gTempTextPrinter = {0}; +EWRAM_DATA struct TextPrinter gTextPrinters[NUM_TEXT_PRINTERS] = {0}; + +static u16 gFontHalfRowLookupTable[0x51]; +static u16 gLastTextBgColor; +static u16 gLastTextFgColor; +static u16 gLastTextShadowColor; + +const struct FontInfo *gFonts; +u8 gDisableTextPrinters; +struct TextGlyph gCurGlyph; +TextFlags gTextFlags; + +const u8 gFontHalfRowOffsets[] = +{ + 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 +}; + +const u8 gDownArrowTiles[] = INCBIN_U8("graphics/fonts/down_arrow.4bpp"); +const u8 gDarkDownArrowTiles[] = INCBIN_U8("graphics/fonts/down_arrow_RS.4bpp"); +const u8 gUnusedFRLGBlankedDownArrow[] = INCBIN_U8("graphics/fonts/unused_frlg_blanked_down_arrow.4bpp"); +const u8 gUnusedFRLGDownArrow[] = INCBIN_U8("graphics/fonts/unused_frlg_down_arrow.4bpp"); +const u8 gDownArrowYCoords[] = { 0x0, 0x1, 0x2, 0x1 }; +const u8 gWindowVerticalScrollSpeeds[] = { 0x1, 0x2, 0x4, 0x0 }; + +const struct GlyphWidthFunc gGlyphWidthFuncs[] = +{ + { 0x0, GetGlyphWidthFont0 }, + { 0x1, GetGlyphWidthFont1 }, + { 0x2, GetGlyphWidthFont2 }, + { 0x3, GetGlyphWidthFont2 }, + { 0x4, GetGlyphWidthFont2 }, + { 0x5, GetGlyphWidthFont2 }, + { 0x6, GetGlyphWidthFont6 }, + { 0x7, GetGlyphWidthFont7 }, + { 0x8, GetGlyphWidthFont8 } +}; + +const struct KeypadIcon gKeypadIcons[] = +{ + [CHAR_A_BUTTON] = { 0x0, 0x8, 0xC }, + [CHAR_B_BUTTON] = { 0x1, 0x8, 0xC }, + [CHAR_L_BUTTON] = { 0x2, 0x10, 0xC }, + [CHAR_R_BUTTON] = { 0x4, 0x10, 0xC }, + [CHAR_START_BUTTON] = { 0x6, 0x18, 0xC }, + [CHAR_SELECT_BUTTON] = { 0x9, 0x18, 0xC }, + [CHAR_DPAD_UP] = { 0xC, 0x8, 0xC }, + [CHAR_DPAD_DOWN] = { 0xD, 0x8, 0xC }, + [CHAR_DPAD_LEFT] = { 0xE, 0x8, 0xC }, + [CHAR_DPAD_RIGHT] = { 0xF, 0x8, 0xC }, + [CHAR_DPAD_UPDOWN] = { 0x20, 0x8, 0xC }, + [CHAR_DPAD_LEFTRIGHT] = { 0x21, 0x8, 0xC }, + [CHAR_DPAD_NONE] = { 0x22, 0x8, 0xC } +}; + +const u8 gKeypadIconTiles[] = INCBIN_U8("graphics/fonts/keypad_icons.4bpp"); + +const struct FontInfo gFontInfos[] = +{ + { Font0Func, 0x5, 0xC, 0x0, 0x0, 0x0, 0x2, 0x1, 0x3 }, + { Font1Func, 0x6, 0x10, 0x0, 0x0, 0x0, 0x2, 0x1, 0x3 }, + { Font2Func, 0x6, 0xE, 0x0, 0x0, 0x0, 0x2, 0x1, 0x3 }, + { Font3Func, 0x6, 0xE, 0x0, 0x0, 0x0, 0x2, 0x1, 0x3 }, + { Font4Func, 0x6, 0xE, 0x0, 0x0, 0x0, 0x2, 0x1, 0x3 }, + { Font5Func, 0x6, 0xE, 0x0, 0x0, 0x0, 0x2, 0x1, 0x3 }, + { Font6Func, 0x8, 0x10, 0x0, 0x8, 0x0, 0x2, 0x1, 0x3 }, + { Font7Func, 0x5, 0x10, 0x0, 0x0, 0x0, 0x2, 0x1, 0x3 }, + { Font8Func, 0x5, 0x8, 0x0, 0x0, 0x0, 0x2, 0x1, 0x3 }, + { NULL, 0x8, 0x8, 0x0, 0x0, 0x0, 0x1, 0x2, 0xF } +}; + +const u8 gMenuCursorDimensions[][2] = +{ + { 0x8, 0xC }, + { 0x8, 0xF }, + { 0x8, 0xE }, + { 0x8, 0xE }, + { 0x8, 0xE }, + { 0x8, 0xE }, + { 0x8, 0x10 }, + { 0x8, 0xF }, + { 0x8, 0x8 }, + { 0x0, 0x0 } +}; + +const u16 gFont9JapaneseGlyphs[] = INCBIN_U16("graphics/fonts/font9.hwjpnfont"); + +extern const u16 gFont8LatinGlyphs[]; +extern const u8 gFont8LatinGlyphWidths[]; +extern const u16 gFont0LatinGlyphs[]; +extern const u8 gFont0LatinGlyphWidths[]; +extern const u16 gFont7LatinGlyphs[]; +extern const u8 gFont7LatinGlyphWidths[]; +extern const u16 gFont2LatinGlyphs[]; +extern const u8 gFont2LatinGlyphWidths[]; +extern const u16 gFont1LatinGlyphs[]; +extern const u8 gFont1LatinGlyphWidths[]; +extern const u16 gFont0JapaneseGlyphs[]; +extern const u16 gFont1JapaneseGlyphs[]; +extern const u16 gFont2JapaneseGlyphs[]; +extern const u8 gFont2JapaneseGlyphWidths[]; + +void SetFontsPointer(const struct FontInfo *fonts) +{ + gFonts = fonts; +} + +void DeactivateAllTextPrinters(void) +{ + int printer; + for (printer = 0; printer < NUM_TEXT_PRINTERS; ++printer) + gTextPrinters[printer].active = 0; +} + +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; + + gTempTextPrinter.active = 1; + gTempTextPrinter.state = 0; + gTempTextPrinter.textSpeed = speed; + gTempTextPrinter.delayCounter = 0; + gTempTextPrinter.scrollDistance = 0; + + for (i = 0; i < 7; i++) + { + gTempTextPrinter.subStructFields[i] = 0; + } + + gTempTextPrinter.printerTemplate = *printerTemplate; + gTempTextPrinter.callback = callback; + gTempTextPrinter.minLetterSpacing = 0; + gTempTextPrinter.japanese = 0; + + GenerateFontHalfRowLookupTable(printerTemplate->fgColor, printerTemplate->bgColor, printerTemplate->shadowColor); + if (speed != TEXT_SPEED_FF && speed != 0) + { + --gTempTextPrinter.textSpeed; + gTextPrinters[printerTemplate->windowId] = gTempTextPrinter; + } + else + { + gTempTextPrinter.textSpeed = 0; + for (j = 0; j < 0x400; ++j) + { + if (RenderFont(&gTempTextPrinter) == 1) + break; + } + + if (speed != TEXT_SPEED_FF) + CopyWindowToVram(gTempTextPrinter.printerTemplate.windowId, 2); + gTextPrinters[printerTemplate->windowId].active = 0; + } + gDisableTextPrinters = 0; + return TRUE; +} + +void RunTextPrinters(void) +{ + int i; + + if (gDisableTextPrinters == 0) + { + for (i = 0; i < NUM_TEXT_PRINTERS; ++i) + { + if (gTextPrinters[i].active) + { + u16 temp = RenderFont(&gTextPrinters[i]); + switch (temp) + { + case 0: + CopyWindowToVram(gTextPrinters[i].printerTemplate.windowId, 2); + case 3: + if (gTextPrinters[i].callback != 0) + gTextPrinters[i].callback(&gTextPrinters[i].printerTemplate, temp); + break; + case 1: + gTextPrinters[i].active = 0; + break; + } + } + } + } +} + +bool16 IsTextPrinterActive(u8 id) +{ + return gTextPrinters[id].active; +} + +u32 RenderFont(struct TextPrinter *textPrinter) +{ + u32 ret; + while (TRUE) + { + ret = gFonts[textPrinter->printerTemplate.fontId].fontFunction(textPrinter); + if (ret != 2) + return ret; + } +} + +void GenerateFontHalfRowLookupTable(u8 fgColor, u8 bgColor, u8 shadowColor) +{ + u32 fg12, bg12, shadow12; + u32 temp; + + u16 *current = gFontHalfRowLookupTable; + + gLastTextBgColor = bgColor; + gLastTextFgColor = fgColor; + gLastTextShadowColor = 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 = gLastTextBgColor; + *fgColor = gLastTextFgColor; + *shadowColor = gLastTextShadowColor; +} + +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)++ = ((gFontHalfRowLookupTable[gFontHalfRowOffsets[temp & 0xFF]]) << 16) | (gFontHalfRowLookupTable[gFontHalfRowOffsets[temp >> 8]]); + + temp = *(src++); + *(dest++) = ((gFontHalfRowLookupTable[gFontHalfRowOffsets[temp & 0xFF]]) << 16) | (gFontHalfRowLookupTable[gFontHalfRowOffsets[temp >> 8]]); + + temp = *(src++); + *(dest++) = ((gFontHalfRowLookupTable[gFontHalfRowOffsets[temp & 0xFF]]) << 16) | (gFontHalfRowLookupTable[gFontHalfRowOffsets[temp >> 8]]); + + temp = *(src++); + *(dest++) = ((gFontHalfRowLookupTable[gFontHalfRowOffsets[temp & 0xFF]]) << 16) | (gFontHalfRowLookupTable[gFontHalfRowOffsets[temp >> 8]]); + + temp = *(src++); + *(dest++) = ((gFontHalfRowLookupTable[gFontHalfRowOffsets[temp & 0xFF]]) << 16) | (gFontHalfRowLookupTable[gFontHalfRowOffsets[temp >> 8]]); + + temp = *(src++); + *(dest++) = ((gFontHalfRowLookupTable[gFontHalfRowOffsets[temp & 0xFF]]) << 16) | (gFontHalfRowLookupTable[gFontHalfRowOffsets[temp >> 8]]); + + temp = *(src++); + *(dest++) = ((gFontHalfRowLookupTable[gFontHalfRowOffsets[temp & 0xFF]]) << 16) | (gFontHalfRowLookupTable[gFontHalfRowOffsets[temp >> 8]]); + + temp = *(src++); + *(dest++) = ((gFontHalfRowLookupTable[gFontHalfRowOffsets[temp & 0xFF]]) << 16) | (gFontHalfRowLookupTable[gFontHalfRowOffsets[temp >> 8]]); +} + +u8 GetLastTextColor(u8 colorType) +{ + switch (colorType) + { + case 0: + return gLastTextFgColor; + case 2: + return gLastTextBgColor; + case 1: + return gLastTextShadowColor; + 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 (gLastTextBgColor != 0) + { + 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, + gLastTextBgColor); + } +} + +u16 Font0Func(struct TextPrinter *textPrinter) +{ + struct TextPrinterSubStruct *subStruct = (struct TextPrinterSubStruct *)(&textPrinter->subStructFields); + + if (subStruct->hasGlyphIdBeenSet == FALSE) + { + subStruct->glyphId = 0; + subStruct->hasGlyphIdBeenSet = TRUE; + } + return RenderText(textPrinter); +} + +u16 Font1Func(struct TextPrinter *textPrinter) +{ + struct TextPrinterSubStruct *subStruct = (struct TextPrinterSubStruct *)(&textPrinter->subStructFields); + + if (subStruct->hasGlyphIdBeenSet == FALSE) + { + subStruct->glyphId = 1; + subStruct->hasGlyphIdBeenSet = TRUE; + } + return RenderText(textPrinter); +} + +u16 Font2Func(struct TextPrinter *textPrinter) +{ + struct TextPrinterSubStruct *subStruct = (struct TextPrinterSubStruct *)(&textPrinter->subStructFields); + + if (subStruct->hasGlyphIdBeenSet == FALSE) + { + subStruct->glyphId = 2; + subStruct->hasGlyphIdBeenSet = TRUE; + } + return RenderText(textPrinter); +} + +u16 Font3Func(struct TextPrinter *textPrinter) +{ + struct TextPrinterSubStruct *subStruct = (struct TextPrinterSubStruct *)(&textPrinter->subStructFields); + + if (subStruct->hasGlyphIdBeenSet == FALSE) + { + subStruct->glyphId = 3; + subStruct->hasGlyphIdBeenSet = TRUE; + } + return RenderText(textPrinter); +} + +u16 Font4Func(struct TextPrinter *textPrinter) +{ + struct TextPrinterSubStruct *subStruct = (struct TextPrinterSubStruct *)(&textPrinter->subStructFields); + + if (subStruct->hasGlyphIdBeenSet == FALSE) + { + subStruct->glyphId = 4; + subStruct->hasGlyphIdBeenSet = TRUE; + } + return RenderText(textPrinter); +} + +u16 Font5Func(struct TextPrinter *textPrinter) +{ + struct TextPrinterSubStruct *subStruct = (struct TextPrinterSubStruct *)(&textPrinter->subStructFields); + + if (subStruct->hasGlyphIdBeenSet == FALSE) + { + subStruct->glyphId = 5; + subStruct->hasGlyphIdBeenSet = TRUE; + } + return RenderText(textPrinter); +} + +u16 Font7Func(struct TextPrinter *textPrinter) +{ + struct TextPrinterSubStruct *subStruct = (struct TextPrinterSubStruct *)(&textPrinter->subStructFields); + + if (subStruct->hasGlyphIdBeenSet == FALSE) + { + subStruct->glyphId = 7; + subStruct->hasGlyphIdBeenSet = TRUE; + } + return RenderText(textPrinter); +} + +u16 Font8Func(struct TextPrinter *textPrinter) +{ + struct TextPrinterSubStruct *subStruct = (struct TextPrinterSubStruct *)(&textPrinter->subStructFields); + + if (subStruct->hasGlyphIdBeenSet == FALSE) + { + subStruct->glyphId = 8; + subStruct->hasGlyphIdBeenSet = 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 = gDownArrowTiles; + break; + case TRUE: + arrowTiles = gDarkDownArrowTiles; + break; + } + + BlitBitmapRectToWindow( + textPrinter->printerTemplate.windowId, + arrowTiles, + 0, + gDownArrowYCoords[subStruct->downArrowYPosIdx], + 8, + 16, + textPrinter->printerTemplate.currentX, + textPrinter->printerTemplate.currentY, + 8, + 16); + CopyWindowToVram(textPrinter->printerTemplate.windowId, 2); + + 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, 2); +} + +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 0: + default: + arrowTiles = gDownArrowTiles; + break; + case 1: + arrowTiles = gDarkDownArrowTiles; + break; + } + + BlitBitmapRectToWindow( + windowId, + arrowTiles, + 0, + gDownArrowYCoords[*yCoordIndex & 3], + 0x8, + 0x10, + x, + y - 2, + 0x8, + 0x10); + CopyWindowToVram(windowId, 0x2); + *counter = 8; + ++*yCoordIndex; + } + } +} + +u16 RenderText(struct TextPrinter *textPrinter) +{ + struct TextPrinterSubStruct *subStruct = (struct TextPrinterSubStruct *)(&textPrinter->subStructFields); + u16 currChar; + s32 width; + s32 widthHelper; + + switch (textPrinter->state) + { + case 0: + 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 3; + } + + 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 2; + case PLACEHOLDER_BEGIN: + textPrinter->printerTemplate.currentChar++; + return 2; + 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 2; + 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 2; + 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 2; + 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 2; + case EXT_CTRL_CODE_PALETTE: + textPrinter->printerTemplate.currentChar++; + return 2; + case EXT_CTRL_CODE_SIZE: + subStruct->glyphId = *textPrinter->printerTemplate.currentChar; + textPrinter->printerTemplate.currentChar++; + return 2; + case EXT_CTRL_CODE_RESET_SIZE: + return 2; + case EXT_CTRL_CODE_PAUSE: + textPrinter->delayCounter = *textPrinter->printerTemplate.currentChar; + textPrinter->printerTemplate.currentChar++; + textPrinter->state = 6; + return 2; + case EXT_CTRL_CODE_PAUSE_UNTIL_PRESS: + textPrinter->state = 1; + if (gTextFlags.autoScroll) + subStruct->autoScrollDelay = 0; + return 3; + case EXT_CTRL_CODE_WAIT_SE: + textPrinter->state = 5; + return 3; + case EXT_CTRL_CODE_PLAY_BGM: + currChar = *textPrinter->printerTemplate.currentChar; + textPrinter->printerTemplate.currentChar++; + currChar |= *textPrinter->printerTemplate.currentChar << 8; + textPrinter->printerTemplate.currentChar++; + PlayBGM(currChar); + return 2; + 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 2; + case EXT_CTRL_CODE_SHIFT_TEXT: + textPrinter->printerTemplate.currentX = textPrinter->printerTemplate.x + *textPrinter->printerTemplate.currentChar; + textPrinter->printerTemplate.currentChar++; + return 2; + case EXT_CTRL_CODE_SHIFT_DOWN: + textPrinter->printerTemplate.currentY = textPrinter->printerTemplate.y + *textPrinter->printerTemplate.currentChar; + textPrinter->printerTemplate.currentChar++; + return 2; + 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 2; + case EXT_CTRL_CODE_PAUSE_MUSIC: + m4aMPlayStop(&gMPlayInfo_BGM); + return 2; + case EXT_CTRL_CODE_RESUME_MUSIC: + m4aMPlayContinue(&gMPlayInfo_BGM); + return 2; + case EXT_CTRL_CODE_CLEAR: + width = *textPrinter->printerTemplate.currentChar; + textPrinter->printerTemplate.currentChar++; + if (width > 0) + { + ClearTextSpan(textPrinter, width); + textPrinter->printerTemplate.currentX += width; + return 0; + } + return 2; + case EXT_CTRL_CODE_SKIP: + textPrinter->printerTemplate.currentX = *textPrinter->printerTemplate.currentChar + textPrinter->printerTemplate.x; + textPrinter->printerTemplate.currentChar++; + return 2; + 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 0; + } + } + return 2; + case EXT_CTRL_CODE_MIN_LETTER_SPACING: + textPrinter->minLetterSpacing = *textPrinter->printerTemplate.currentChar++; + return 2; + case EXT_CTRL_CODE_JPN: + textPrinter->japanese = 1; + return 2; + case EXT_CTRL_CODE_ENG: + textPrinter->japanese = 0; + return 2; + } + break; + case CHAR_PROMPT_CLEAR: + textPrinter->state = 2; + TextPrinterInitDownArrowCounters(textPrinter); + return 3; + case CHAR_PROMPT_SCROLL: + textPrinter->state = 3; + TextPrinterInitDownArrowCounters(textPrinter); + return 3; + 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 0; + case EOS: + return 1; + } + + switch (subStruct->glyphId) + { + case 0: + DecompressGlyphFont0(currChar, textPrinter->japanese); + break; + case 1: + DecompressGlyphFont1(currChar, textPrinter->japanese); + break; + case 2: + case 3: + case 4: + case 5: + DecompressGlyphFont2(currChar, textPrinter->japanese); + break; + case 7: + DecompressGlyphFont7(currChar, textPrinter->japanese); + break; + case 8: + DecompressGlyphFont8(currChar, textPrinter->japanese); + break; + case 6: + 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 0; + case 1: + if (TextPrinterWait(textPrinter)) + textPrinter->state = 0; + return 3; + case 2: + 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 = 0; + } + return 3; + case 3: + if (TextPrinterWaitWithDownArrow(textPrinter)) + { + TextPrinterClearDownArrow(textPrinter); + textPrinter->scrollDistance = gFonts[textPrinter->printerTemplate.fontId].maxLetterHeight + textPrinter->printerTemplate.lineSpacing; + textPrinter->printerTemplate.currentX = textPrinter->printerTemplate.x; + textPrinter->state = 4; + } + return 3; + case 4: + if (textPrinter->scrollDistance) + { + int scrollSpeed = GetPlayerTextSpeed(); + int speed = gWindowVerticalScrollSpeeds[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, 2); + } + else + { + textPrinter->state = 0; + } + return 3; + case 5: + if (!IsSEPlaying()) + textPrinter->state = 0; + return 3; + case 6: + if (textPrinter->delayCounter != 0) + textPrinter->delayCounter--; + else + textPrinter->state = 0; + return 3; + } + + return 1; +} + +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 < 8; 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_SIZE: + 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; +} + +u32 (*GetFontWidthFunc(u8 glyphId))(u16, bool32) +{ + u32 i; + + for (i = 0; i < 9; ++i) + { + if (glyphId == gGlyphWidthFuncs[i].fontId) + return gGlyphWidthFuncs[i].func; + } + + return NULL; +} + +s32 GetStringWidth(u8 fontId, const u8 *str, s16 letterSpacing) +{ + bool8 isJapanese; + int minGlyphWidth; + u32 (*func)(u16 glyphId, 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_SIZE: + 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 RenderTextFont9(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_SIZE: + 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 9: + DecompressGlyphFont9(temp); + break; + case 1: + default: + DecompressGlyphFont1(temp, 1); + 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, + gKeypadIconTiles + (gKeypadIcons[keypadIconId].tileOffset * 0x20), + 0, + 0, + 0x80, + 0x80, + x, + y, + gKeypadIcons[keypadIconId].width, + gKeypadIcons[keypadIconId].height); + return gKeypadIcons[keypadIconId].width; +} + +u8 GetKeypadIconTileOffset(u8 keypadIconId) +{ + return gKeypadIcons[keypadIconId].tileOffset; +} + +u8 GetKeypadIconWidth(u8 keypadIconId) +{ + return gKeypadIcons[keypadIconId].width; +} + +u8 GetKeypadIconHeight(u8 keypadIconId) +{ + return gKeypadIcons[keypadIconId].height; +} + +void SetDefaultFontsPointer(void) +{ + SetFontsPointer(&gFontInfos[0]); +} + +u8 GetFontAttribute(u8 fontId, u8 attributeId) +{ + int result = 0; + switch (attributeId) + { + case FONTATTR_MAX_LETTER_WIDTH: + result = gFontInfos[fontId].maxLetterWidth; + break; + case FONTATTR_MAX_LETTER_HEIGHT: + result = gFontInfos[fontId].maxLetterHeight; + break; + case FONTATTR_LETTER_SPACING: + result = gFontInfos[fontId].letterSpacing; + break; + case FONTATTR_LINE_SPACING: + result = gFontInfos[fontId].lineSpacing; + break; + case FONTATTR_UNKNOWN: + result = gFontInfos[fontId].unk; + break; + case FONTATTR_COLOR_FOREGROUND: + result = gFontInfos[fontId].fgColor; + break; + case FONTATTR_COLOR_BACKGROUND: + result = gFontInfos[fontId].bgColor; + break; + case FONTATTR_COLOR_SHADOW: + result = gFontInfos[fontId].shadowColor; + break; + } + return result; +} + +u8 GetMenuCursorDimensionByFont(u8 fontId, u8 whichDimension) +{ + return gMenuCursorDimensions[fontId][whichDimension]; +} + +void DecompressGlyphFont0(u16 glyphId, bool32 isJapanese) +{ + const u16* glyphs; + + if (isJapanese == 1) + { + glyphs = gFont0JapaneseGlyphs + (0x100 * (glyphId >> 0x4)) + (0x8 * (glyphId & 0xF)); + DecompressGlyphTile(glyphs, gCurGlyph.gfxBufferTop); + DecompressGlyphTile(glyphs + 0x80, gCurGlyph.gfxBufferBottom); + gCurGlyph.width = 8; + gCurGlyph.height = 12; + } + else + { + glyphs = gFont0LatinGlyphs + (0x20 * glyphId); + gCurGlyph.width = gFont0LatinGlyphWidths[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; + } +} + +u32 GetGlyphWidthFont0(u16 glyphId, bool32 isJapanese) +{ + if (isJapanese == TRUE) + return 8; + else + return gFont0LatinGlyphWidths[glyphId]; +} + +void DecompressGlyphFont7(u16 glyphId, bool32 isJapanese) +{ + const u16* glyphs; + + if (isJapanese == TRUE) + { + glyphs = gFont1JapaneseGlyphs + (0x100 * (glyphId >> 0x4)) + (0x8 * (glyphId % 0x10)); + DecompressGlyphTile(glyphs, gCurGlyph.gfxBufferTop); + DecompressGlyphTile(glyphs + 0x80, gCurGlyph.gfxBufferBottom); + gCurGlyph.width = 8; + gCurGlyph.height = 15; + } + else + { + glyphs = gFont7LatinGlyphs + (0x20 * glyphId); + gCurGlyph.width = gFont7LatinGlyphWidths[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; + } +} + +u32 GetGlyphWidthFont7(u16 glyphId, bool32 isJapanese) +{ + if (isJapanese == TRUE) + return 8; + else + return gFont7LatinGlyphWidths[glyphId]; +} + +void DecompressGlyphFont8(u16 glyphId, bool32 isJapanese) +{ + const u16* glyphs; + + if (isJapanese == TRUE) + { + glyphs = gFont0JapaneseGlyphs + (0x100 * (glyphId >> 0x4)) + (0x8 * (glyphId & 0xF)); + DecompressGlyphTile(glyphs, gCurGlyph.gfxBufferTop); + DecompressGlyphTile(glyphs + 0x80, gCurGlyph.gfxBufferBottom); + gCurGlyph.width = 8; + gCurGlyph.height = 12; + } + else + { + glyphs = gFont8LatinGlyphs + (0x20 * glyphId); + gCurGlyph.width = gFont8LatinGlyphWidths[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; + } +} + +u32 GetGlyphWidthFont8(u16 glyphId, bool32 isJapanese) +{ + if (isJapanese == TRUE) + return 8; + else + return gFont8LatinGlyphWidths[glyphId]; +} + +void DecompressGlyphFont2(u16 glyphId, bool32 isJapanese) +{ + const u16* glyphs; + + if (isJapanese == TRUE) + { + glyphs = gFont2JapaneseGlyphs + (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 = gFont2JapaneseGlyphWidths[glyphId]; + gCurGlyph.height = 14; + } + else + { + glyphs = gFont2LatinGlyphs + (0x20 * glyphId); + gCurGlyph.width = gFont2LatinGlyphWidths[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; + } +} + +u32 GetGlyphWidthFont2(u16 glyphId, bool32 isJapanese) +{ + if (isJapanese == TRUE) + return gFont2JapaneseGlyphWidths[glyphId]; + else + return gFont2LatinGlyphWidths[glyphId]; +} + +void DecompressGlyphFont1(u16 glyphId, bool32 isJapanese) +{ + const u16* glyphs; + + if (isJapanese == TRUE) + { + glyphs = gFont1JapaneseGlyphs + (0x100 * (glyphId >> 0x4)) + (0x8 * (glyphId % 0x10)); + DecompressGlyphTile(glyphs, gCurGlyph.gfxBufferTop); + DecompressGlyphTile(glyphs + 0x80, gCurGlyph.gfxBufferBottom); + gCurGlyph.width = 8; + gCurGlyph.height = 15; + } + else + { + glyphs = gFont1LatinGlyphs + (0x20 * glyphId); + gCurGlyph.width = gFont1LatinGlyphWidths[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; + } +} + +u32 GetGlyphWidthFont1(u16 glyphId, bool32 isJapanese) +{ + if (isJapanese == TRUE) + return 8; + else + return gFont1LatinGlyphWidths[glyphId]; +} + +void DecompressGlyphFont9(u16 glyphId) +{ + const u16* glyphs; + + glyphs = gFont9JapaneseGlyphs + (0x100 * (glyphId >> 4)) + (0x8 * (glyphId & 0xF)); + DecompressGlyphTile(glyphs, gCurGlyph.gfxBufferTop); + DecompressGlyphTile(glyphs + 0x80, gCurGlyph.gfxBufferBottom); + gCurGlyph.width = 8; + gCurGlyph.height = 12; +} diff --git a/src/window.c b/src/window.c new file mode 100644 index 000000000..b03b513da --- /dev/null +++ b/src/window.c @@ -0,0 +1,721 @@ +#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 gUnneededFireRedVariable; + +#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 (gUnneededFireRedVariable == 1) + { + allocatedBaseBlock = DummiedOutFireRedLeafGreenTileAllocFunc(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 (gUnneededFireRedVariable == 1) + { + gWindows[i].window.baseBlock = allocatedBaseBlock; + DummiedOutFireRedLeafGreenTileAllocFunc(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 (gUnneededFireRedVariable == 1) + { + allocatedBaseBlock = DummiedOutFireRedLeafGreenTileAllocFunc(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 (gUnneededFireRedVariable == 1) + { + gWindows[win].window.baseBlock = allocatedBaseBlock; + DummiedOutFireRedLeafGreenTileAllocFunc(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 (gUnneededFireRedVariable == 1) + { + allocatedBaseBlock = DummiedOutFireRedLeafGreenTileAllocFunc(bgLayer, 0, template->width * template->height, 0); + + if (allocatedBaseBlock == -1) + return WINDOW_NONE; + } + + gWindows[win].window = *template; + + if (gUnneededFireRedVariable == 1) + { + gWindows[win].window.baseBlock = allocatedBaseBlock; + DummiedOutFireRedLeafGreenTileAllocFunc(bgLayer, allocatedBaseBlock, gWindows[win].window.width * gWindows[win].window.height, 1); + } + + return win; +} + +void RemoveWindow(u8 windowId) +{ + u8 bgLayer = gWindows[windowId].window.bg; + + if (gUnneededFireRedVariable == 1) + { + DummiedOutFireRedLeafGreenTileAllocFunc(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 1: + CopyBgTilemapBufferToVram(windowLocal.window.bg); + break; + case 2: + LoadBgTiles(windowLocal.window.bg, windowLocal.tileData, windowSize, windowLocal.window.baseBlock); + break; + case 3: + 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 1: + CopyBgTilemapBufferToVram(windowLocal.window.bg); + break; + case 2: + LoadBgTiles(windowLocal.window.bg, windowLocal.tileData + (rectPos * 32), rectSize, windowLocal.window.baseBlock + rectPos); + break; + case 3: + 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 1: + CopyBgTilemapBufferToVram(sWindowPtr->window.bg); + break; + case 2: + LoadBgTiles(sWindowPtr->window.bg, sWindowPtr->tileData, sWindowSize, sWindowPtr->window.baseBlock); + break; + case 3: + 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; +} |